载入中...
搜索中...
未找到
PointSet.cpp
浏览该文件的文档.
1#include "procgen/PointSet.h"
2
3#include "common/Diagnostic.h"
4#include "procgen/Grid2D.h"
5
6#include <algorithm>
7#include <bit>
8#include <cmath>
9#include <limits>
10#include <random>
11#include <unordered_set>
12
13namespace eve::procgen {
14namespace {
15
16uint32_t mix32(uint32_t value) {
17 value += 0x9e3779b9u;
18 value = (value ^ (value >> 16u)) * 0x21f0aaadu;
19 value = (value ^ (value >> 15u)) * 0x735a2d97u;
20 return value ^ (value >> 15u);
21}
22
23float unitFloat(uint32_t seed) { return float(mix32(seed) >> 8u) * (1.f / 16777216.f); }
24
25float pointSegmentDistanceSquared(float px, float pz, const ProcgenPoint& a, const ProcgenPoint& b) {
26 const float vx = b.x - a.x;
27 const float vz = b.z - a.z;
28 const float length2 = vx * vx + vz * vz;
29 const float t = length2 > 0.f ? std::clamp(((px - a.x) * vx + (pz - a.z) * vz) / length2, 0.f, 1.f) : 0.f;
30 const float dx = px - (a.x + vx * t);
31 const float dz = pz - (a.z + vz * t);
32 return dx * dx + dz * dz;
33}
34
35bool pointInPolygon(float x, float z, const std::vector<ProcgenPoint>& polygon) {
36 bool inside = false;
37 for (size_t i = 0, previous = polygon.size() - 1; i < polygon.size(); previous = i++) {
38 const auto& a = polygon[i];
39 const auto& b = polygon[previous];
40 const bool crosses = ((a.z > z) != (b.z > z)) && (x < (b.x - a.x) * (z - a.z) / (b.z - a.z) + a.x);
41 if (crosses) inside = !inside;
42 }
43 return inside;
44}
45
46} // namespace
47
48int PointSet::getCount() const { return int(points_.size()); }
49bool PointSet::empty() const { return points_.empty(); }
51 points_.clear();
52 attributes_.clear();
53 dataAttributes_.clear();
54}
55
56void appendPointRow(PointSet& output, const PointSet& input, std::size_t index) {
57 std::move(output.appendPointFrom(input, index)).expect("PointSet operation requires compatible attribute schemas");
58}
59
60void PointSet::reserve(std::size_t count) { points_.reserve(count); }
61
63 points_.push_back(std::move(point));
64 const std::size_t row = attributes_.appendRow();
65 EV_ASSERT(row + 1 == points_.size(), "PointSet point and attribute rows must remain aligned");
66 return int(row);
67}
68
69Result<int> PointSet::appendPointFrom(const PointSet& source, std::size_t sourceIndex) {
70 if (sourceIndex >= source.points_.size())
72 Diagnostic::error(DiagnosticCode::InvalidArgument, "source point index is out of range", "sourceIndex"));
73 auto attributeRow = attributes_.appendRowFrom(source.attributes_, sourceIndex);
74 if (!attributeRow.ok()) return Result<int>::failure(attributeRow.status());
75 try {
76 points_.push_back(source.points_[sourceIndex]);
77 } catch (...) {
78 attributes_.resize(attributeRow.value());
79 throw;
80 }
81 EV_ASSERT(attributeRow.value() + 1 == points_.size(), "PointSet point and attribute rows must remain aligned");
82 return Result<int>::success(int(attributeRow.value()));
83}
84
86 if (index >= points_.size())
88 Diagnostic::error(DiagnosticCode::InvalidArgument, "point index is out of range", "index"));
89 return attributes_.clearRow(index);
90}
91
93 EV_PARAM_CHECK(index < points_.size(), "PointSet mutable point index is out of range");
94 return points_[index];
95}
96
97int PointSet::add(float x, float y, float z) {
99 point.x = x;
100 point.y = y;
101 point.z = z;
102 return appendPoint(std::move(point));
103}
104
105ProcgenPoint* PointSet::pointAt(int index) {
106 return index >= 0 && index < int(points_.size()) ? &points_[size_t(index)] : nullptr;
107}
108
109const ProcgenPoint* PointSet::pointAt(int index) const {
110 return index >= 0 && index < int(points_.size()) ? &points_[size_t(index)] : nullptr;
111}
112
113void PointSet::setPosition(int index, float x, float y, float z) {
114 if (auto* point = pointAt(index)) {
115 point->x = x;
116 point->y = y;
117 point->z = z;
118 }
119}
120
121float PointSet::getX(int index) const {
122 const auto* p = pointAt(index);
123 return p ? p->x : 0.f;
124}
125float PointSet::getY(int index) const {
126 const auto* p = pointAt(index);
127 return p ? p->y : 0.f;
128}
129float PointSet::getZ(int index) const {
130 const auto* p = pointAt(index);
131 return p ? p->z : 0.f;
132}
133
134void PointSet::setNormal(int index, float x, float y, float z) {
135 if (auto* point = pointAt(index)) {
136 point->normalX = x;
137 point->normalY = y;
138 point->normalZ = z;
139 }
140}
141
142float PointSet::getNormalX(int index) const {
143 const auto* p = pointAt(index);
144 return p ? p->normalX : 0.f;
145}
146float PointSet::getNormalY(int index) const {
147 const auto* p = pointAt(index);
148 return p ? p->normalY : 1.f;
149}
150float PointSet::getNormalZ(int index) const {
151 const auto* p = pointAt(index);
152 return p ? p->normalZ : 0.f;
153}
154
155void PointSet::setYaw(int index, float yaw) {
156 if (auto* p = pointAt(index)) p->yaw = yaw;
157}
158float PointSet::getYaw(int index) const {
159 const auto* p = pointAt(index);
160 return p ? p->yaw : 0.f;
161}
162
163void PointSet::setRotation(int index, float pitch, float yaw, float roll) {
164 if (auto* point = pointAt(index)) {
165 point->pitch = pitch;
166 point->yaw = yaw;
167 point->roll = roll;
168 }
169}
170
171float PointSet::getPitch(int index) const {
172 const auto* point = pointAt(index);
173 return point ? point->pitch : 0.f;
174}
175
176float PointSet::getRoll(int index) const {
177 const auto* point = pointAt(index);
178 return point ? point->roll : 0.f;
179}
180
181void PointSet::setScale(int index, float x, float y, float z) {
182 if (auto* point = pointAt(index)) {
183 point->scaleX = x;
184 point->scaleY = y;
185 point->scaleZ = z;
186 }
187}
188
189float PointSet::getScaleX(int index) const {
190 const auto* p = pointAt(index);
191 return p ? p->scaleX : 1.f;
192}
193float PointSet::getScaleY(int index) const {
194 const auto* p = pointAt(index);
195 return p ? p->scaleY : 1.f;
196}
197float PointSet::getScaleZ(int index) const {
198 const auto* p = pointAt(index);
199 return p ? p->scaleZ : 1.f;
200}
201
202void PointSet::setBounds(int index, float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
203 if (auto* point = pointAt(index)) {
204 point->boundsMinX = std::min(minX, maxX);
205 point->boundsMinY = std::min(minY, maxY);
206 point->boundsMinZ = std::min(minZ, maxZ);
207 point->boundsMaxX = std::max(minX, maxX);
208 point->boundsMaxY = std::max(minY, maxY);
209 point->boundsMaxZ = std::max(minZ, maxZ);
210 }
211}
212
214 const auto* point = pointAt(index);
215 return point ? point->boundsMinX : 0.f;
216}
218 const auto* point = pointAt(index);
219 return point ? point->boundsMinY : 0.f;
220}
222 const auto* point = pointAt(index);
223 return point ? point->boundsMinZ : 0.f;
224}
226 const auto* point = pointAt(index);
227 return point ? point->boundsMaxX : 0.f;
228}
230 const auto* point = pointAt(index);
231 return point ? point->boundsMaxY : 0.f;
232}
234 const auto* point = pointAt(index);
235 return point ? point->boundsMaxZ : 0.f;
236}
237
238void PointSet::setColor(int index, float red, float green, float blue, float alpha) {
239 if (auto* point = pointAt(index)) {
240 point->colorR = std::clamp(red, 0.f, 1.f);
241 point->colorG = std::clamp(green, 0.f, 1.f);
242 point->colorB = std::clamp(blue, 0.f, 1.f);
243 point->colorA = std::clamp(alpha, 0.f, 1.f);
244 }
245}
246
247float PointSet::getColorR(int index) const {
248 const auto* point = pointAt(index);
249 return point ? point->colorR : 1.f;
250}
251float PointSet::getColorG(int index) const {
252 const auto* point = pointAt(index);
253 return point ? point->colorG : 1.f;
254}
255float PointSet::getColorB(int index) const {
256 const auto* point = pointAt(index);
257 return point ? point->colorB : 1.f;
258}
259float PointSet::getColorA(int index) const {
260 const auto* point = pointAt(index);
261 return point ? point->colorA : 1.f;
262}
263
264void PointSet::setSteepness(int index, float steepness) {
265 if (auto* point = pointAt(index)) point->steepness = std::clamp(steepness, 0.f, 1.f);
266}
267
269 const auto* point = pointAt(index);
270 return point ? point->steepness : 0.f;
271}
272
273void PointSet::setDensity(int index, float density) {
274 if (auto* point = pointAt(index)) point->density = std::clamp(density, 0.f, 1.f);
275}
276
277float PointSet::getDensity(int index) const {
278 const auto* p = pointAt(index);
279 return p ? p->density : 0.f;
280}
281void PointSet::setPointSeed(int index, uint32_t seed) {
282 if (auto* p = pointAt(index)) p->seed = seed ? seed : 1u;
283}
284uint32_t PointSet::getPointSeed(int index) const {
285 const auto* p = pointAt(index);
286 return p ? p->seed : 0u;
287}
288
289std::uint64_t PointSet::getPointId(int index) const {
290 const auto* point = pointAt(index);
291 return point ? point->id : 0;
292}
293
295 auto* point = pointAt(index);
296 if (!point)
298 Diagnostic::error(DiagnosticCode::InvalidArgument, "point index is out of range", "index"));
299 if (id == 0)
301 Diagnostic::error(DiagnosticCode::InvalidArgument, "point id must be non-zero", "id"));
302 for (std::size_t row = 0; row < points_.size(); ++row)
303 if (row != std::size_t(index) && points_[row].id == id)
305 Diagnostic::error(DiagnosticCode::Conflict, "point id is already present", "id"));
306 point->id = id;
307 return Result<void>::success();
308}
309
310Result<void> PointSet::assignPointIds(std::uint64_t namespaceId) {
311 if (namespaceId == 0)
313 DiagnosticCode::InvalidArgument, "point id namespace must be non-zero", "namespaceId"));
314 std::unordered_set<std::uint64_t> occupied;
315 occupied.reserve(points_.size());
316 for (const auto& point : points_)
317 if (point.id != 0 && !occupied.insert(point.id).second)
319 Diagnostic::error(DiagnosticCode::Conflict, "point set contains duplicate ids", "points"));
320 PointSet staged = *this;
321 for (std::size_t row = 0; row < staged.points_.size(); ++row) {
322 if (staged.points_[row].id != 0) continue;
323 std::uint64_t candidate = derivePointId(namespaceId, row);
324 while (!occupied.insert(candidate).second) candidate = derivePointId(candidate, row + 1);
325 staged.points_[row].id = candidate;
326 }
327 *this = std::move(staged);
328 return Result<void>::success();
329}
330
332 if (!pointAt(index))
334 Diagnostic::error(DiagnosticCode::InvalidArgument, "point index is out of range", "index"));
335 return attributes_.setFloat(size_t(index), name, value);
336}
337
338void PointSet::setFloatAttribute(int index, const std::string& name, float value) {
339 trySetFloatAttribute(index, name, value).ignore("compatibility PointSet float attribute setter");
340}
341
342float PointSet::getFloatAttribute(int index, const std::string& name, float fallback) const {
343 const auto value = index >= 0 ? attributes_.getFloat(size_t(index), name) : std::nullopt;
344 return value.value_or(fallback);
345}
346
347bool PointSet::hasFloatAttribute(int index, const std::string& name) const {
348 return index >= 0 && attributes_.getFloat(size_t(index), name).has_value();
349}
350
351Result<void> PointSet::trySetIntAttribute(int index, const std::string& name, std::int64_t value) {
352 if (!pointAt(index))
354 Diagnostic::error(DiagnosticCode::InvalidArgument, "point index is out of range", "index"));
355 return attributes_.setInt(size_t(index), name, value);
356}
357
358void PointSet::setIntAttribute(int index, const std::string& name, std::int64_t value) {
359 trySetIntAttribute(index, name, value).ignore("compatibility PointSet integer attribute setter");
360}
361
362std::int64_t PointSet::getIntAttribute(int index, const std::string& name, std::int64_t fallback) const {
363 const auto value = index >= 0 ? attributes_.getInt(size_t(index), name) : std::nullopt;
364 return value.value_or(fallback);
365}
366
367bool PointSet::hasIntAttribute(int index, const std::string& name) const {
368 return index >= 0 && attributes_.getInt(size_t(index), name).has_value();
369}
370
372 if (!pointAt(index))
374 Diagnostic::error(DiagnosticCode::InvalidArgument, "point index is out of range", "index"));
375 return attributes_.setBool(size_t(index), name, value);
376}
377
378void PointSet::setBoolAttribute(int index, const std::string& name, bool value) {
379 trySetBoolAttribute(index, name, value).ignore("compatibility PointSet Boolean attribute setter");
380}
381
382bool PointSet::getBoolAttribute(int index, const std::string& name, bool fallback) const {
383 const auto value = index >= 0 ? attributes_.getBool(size_t(index), name) : std::nullopt;
384 return value.value_or(fallback);
385}
386
387bool PointSet::hasBoolAttribute(int index, const std::string& name) const {
388 return index >= 0 && attributes_.getBool(size_t(index), name).has_value();
389}
390
391Result<void> PointSet::trySetVectorAttribute(int index, const std::string& name, float x, float y, float z) {
392 if (!pointAt(index))
394 Diagnostic::error(DiagnosticCode::InvalidArgument, "point index is out of range", "index"));
395 return attributes_.setVector(size_t(index), name, {x, y, z});
396}
397
398void PointSet::setVectorAttribute(int index, const std::string& name, float x, float y, float z) {
399 trySetVectorAttribute(index, name, x, y, z).ignore("compatibility PointSet vector attribute setter");
400}
401
402float PointSet::getVectorAttributeX(int index, const std::string& name, float fallback) const {
403 const auto value = index >= 0 ? attributes_.getVector(size_t(index), name) : std::nullopt;
404 return value ? value->x : fallback;
405}
406
407float PointSet::getVectorAttributeY(int index, const std::string& name, float fallback) const {
408 const auto value = index >= 0 ? attributes_.getVector(size_t(index), name) : std::nullopt;
409 return value ? value->y : fallback;
410}
411
412float PointSet::getVectorAttributeZ(int index, const std::string& name, float fallback) const {
413 const auto value = index >= 0 ? attributes_.getVector(size_t(index), name) : std::nullopt;
414 return value ? value->z : fallback;
415}
416
417bool PointSet::hasVectorAttribute(int index, const std::string& name) const {
418 return index >= 0 && attributes_.getVector(size_t(index), name).has_value();
419}
420
421Result<void> PointSet::trySetStringAttribute(int index, const std::string& name, const std::string& value) {
422 if (!pointAt(index))
424 Diagnostic::error(DiagnosticCode::InvalidArgument, "point index is out of range", "index"));
425 return attributes_.setString(size_t(index), name, value);
426}
427
428void PointSet::setStringAttribute(int index, const std::string& name, const std::string& value) {
429 trySetStringAttribute(index, name, value).ignore("compatibility PointSet string attribute setter");
430}
431
432std::string PointSet::getStringAttribute(int index, const std::string& name, const std::string& fallback) const {
433 const auto value = index >= 0 ? attributes_.getString(size_t(index), name) : std::nullopt;
434 return value ? std::string(*value) : fallback;
435}
436
437bool PointSet::hasStringAttribute(int index, const std::string& name) const {
438 return index >= 0 && attributes_.getString(size_t(index), name).has_value();
439}
440
441Result<void> PointSet::tryRenameAttribute(const std::string& from, const std::string& to) {
442 return attributes_.renameColumn(from, to);
443}
444
446 return attributes_.removeColumn(name);
447}
448
449Result<void> PointSet::tryCopyAttribute(const std::string& from, const std::string& to) {
450 return attributes_.copyColumn(from, to);
451}
452
453std::string PointSet::getAttributeType(int index, const std::string& name) const {
454 if (!pointAt(index) || !attributes_.has(size_t(index), name)) return {};
455 const auto type = attributes_.typeOf(name);
456 return type ? std::string(procgenAttributeTypeName(*type)) : std::string{};
457}
458
459uint32_t deriveSeed(uint32_t parent, const std::string& scope) {
460 uint32_t hash = 2166136261u ^ parent;
461 for (const unsigned char ch : scope) {
462 hash ^= ch;
463 hash *= 16777619u;
464 }
465 hash = mix32(hash);
466 return hash ? hash : 1u;
467}
468
469std::uint64_t derivePointId(std::uint64_t namespaceId, std::uint64_t ordinal) {
470 std::uint64_t value = namespaceId ^ (ordinal + 0x9e3779b97f4a7c15ull + (namespaceId << 6u) +
471 (namespaceId >> 2u));
472 value ^= value >> 30u;
473 value *= 0xbf58476d1ce4e5b9ull;
474 value ^= value >> 27u;
475 value *= 0x94d049bb133111ebull;
476 value ^= value >> 31u;
477 return value == 0 ? 1 : value;
478}
479
480PointSet sampleGridPoints(int width, int depth, float spacing, uint32_t seed, float jitter) {
482 if (width <= 0 || depth <= 0 || spacing <= 0.f) return output;
483 jitter = std::clamp(jitter, 0.f, 1.f);
484 output.reserve(size_t(width) * size_t(depth));
485 const float extent = spacing * jitter * 0.5f;
486 for (int z = 0; z < depth; ++z) {
487 for (int x = 0; x < width; ++x) {
488 const uint32_t pointSeed = mix32(seed ^ uint32_t(z * width + x));
490 point.x = float(x) * spacing + (unitFloat(pointSeed) * 2.f - 1.f) * extent;
491 point.z = float(z) * spacing + (unitFloat(pointSeed ^ 0xa511e9b3u) * 2.f - 1.f) * extent;
492 point.seed = pointSeed ? pointSeed : 1u;
493 point.id = derivePointId((std::uint64_t(seed) << 32u) | 0x47524944u,
494 std::uint64_t(z) * std::uint64_t(width) + std::uint64_t(x));
495 const int appended = output.appendPoint(std::move(point));
496 (void)appended;
497 }
498 }
499 return output;
500}
501
502PointSet poissonDiskPoints(int width, int depth, float radius, uint32_t seed, int maxPoints) {
504 if (width <= 0 || depth <= 0 || radius <= 0.f) return output;
505 maxPoints = std::max(0, maxPoints);
506
507 // Bridson's algorithm: candidate annulus [r, 2r], grid cell side r / sqrt(2).
508 const float cell = radius * 0.70710678118f;
509 const int gridW = std::max(1, int(std::ceil(float(width) / cell)));
510 const int gridH = std::max(1, int(std::ceil(float(depth) / cell)));
511 std::vector<int> grid(size_t(gridW) * size_t(gridH), -1);
512
513 std::vector<float> xs, ys;
514 std::mt19937 rng(seed);
515 std::uniform_real_distribution<float> unit(0.f, 1.f);
516
517 const auto cellIndex = [&](float x, float y) -> int {
518 int gx = int(x / cell);
519 int gy = int(y / cell);
520 gx = std::max(0, std::min(gx, gridW - 1));
521 gy = std::max(0, std::min(gy, gridH - 1));
522 return gy * gridW + gx;
523 };
524 const auto inBounds = [&](float x, float y) {
525 return x >= 0.f && x <= float(width) && y >= 0.f && y <= float(depth);
526 };
527 const auto tooClose = [&](float x, float y, float r2) {
528 const int gx = std::max(0, std::min(int(x / cell), gridW - 1));
529 const int gy = std::max(0, std::min(int(y / cell), gridH - 1));
530 for (int oy = -2; oy <= 2; ++oy) {
531 for (int ox = -2; ox <= 2; ++ox) {
532 const int nx = gx + ox, ny = gy + oy;
533 if (nx < 0 || nx >= gridW || ny < 0 || ny >= gridH) continue;
534 const int idx = grid[ny * gridW + nx];
535 if (idx < 0) continue;
536 const float dx = x - xs[size_t(idx)], dy = y - ys[size_t(idx)];
537 if (dx * dx + dy * dy < r2) return true;
538 }
539 }
540 return false;
541 };
542
543 // Seed with one random interior point.
544 float fx = unit(rng) * float(width);
545 float fy = unit(rng) * float(depth);
546 xs.push_back(fx);
547 ys.push_back(fy);
548 grid[cellIndex(fx, fy)] = 0;
549 std::vector<int> active{0};
550
551 const float r2 = radius * radius;
552 constexpr int kTries = 30;
553 while (!active.empty() && int(xs.size()) < maxPoints) {
554 const int pick = int(unit(rng) * float(active.size()));
555 const int base = active[size_t(std::min(pick, int(active.size()) - 1))];
556 bool found = false;
557 for (int k = 0; k < kTries; ++k) {
558 const float theta = unit(rng) * 6.28318530718f;
559 const float dist = radius * (1.f + unit(rng));
560 const float nx = xs[size_t(base)] + std::cos(theta) * dist;
561 const float ny = ys[size_t(base)] + std::sin(theta) * dist;
562 if (!inBounds(nx, ny) || tooClose(nx, ny, r2)) continue;
563 xs.push_back(nx);
564 ys.push_back(ny);
565 grid[cellIndex(nx, ny)] = int(xs.size()) - 1;
566 active.push_back(int(xs.size()) - 1);
567 found = true;
568 break;
569 }
570 if (!found) active.erase(active.begin() + pick);
571 }
572
573 output.reserve(xs.size());
574 for (size_t i = 0; i < xs.size(); ++i) {
576 point.x = xs[i];
577 point.y = 0.f;
578 point.z = ys[i];
579 point.seed = mix32(seed ^ uint32_t(i));
580 if (point.seed == 0) point.seed = 1;
581 point.id = derivePointId((std::uint64_t(seed) << 32u) | 0x504f4953u, i);
582 const int appended = output.appendPoint(std::move(point));
583 (void)appended;
584 }
585 return output;
586}
587
588PointSet filterPointHeight(const PointSet& input, float minHeight, float maxHeight) {
590 if (minHeight > maxHeight) std::swap(minHeight, maxHeight);
591 for (size_t index = 0; index < input.points().size(); ++index)
592 if (input.points()[index].y >= minHeight && input.points()[index].y <= maxHeight)
594 return output;
595}
596
597PointSet filterPointDensity(const PointSet& input, float minDensity, float maxDensity) {
599 if (minDensity > maxDensity) std::swap(minDensity, maxDensity);
600 for (size_t index = 0; index < input.points().size(); ++index)
601 if (input.points()[index].density >= minDensity && input.points()[index].density <= maxDensity)
603 return output;
604}
605
606PointSet filterPointBox(const PointSet& input, float minX, float minY, float minZ, float maxX, float maxY, float maxZ,
607 bool invert) {
608 if (minX > maxX) std::swap(minX, maxX);
609 if (minY > maxY) std::swap(minY, maxY);
610 if (minZ > maxZ) std::swap(minZ, maxZ);
612 for (size_t index = 0; index < input.points().size(); ++index) {
613 const auto& point = input.points()[index];
614 const bool inside = point.x >= minX && point.x <= maxX && point.y >= minY && point.y <= maxY &&
615 point.z >= minZ && point.z <= maxZ;
616 if (inside != invert) appendPointRow(output, input, index);
617 }
618 return output;
619}
620
621PointSet filterPointSlope(const PointSet& input, float minDegrees, float maxDegrees) {
622 if (minDegrees > maxDegrees) std::swap(minDegrees, maxDegrees);
623 minDegrees = std::clamp(minDegrees, 0.f, 180.f);
624 maxDegrees = std::clamp(maxDegrees, 0.f, 180.f);
625 constexpr float radiansToDegrees = 57.29577951308232f;
627 for (size_t index = 0; index < input.points().size(); ++index) {
628 const auto& point = input.points()[index];
629 const float length =
630 std::sqrt(point.normalX * point.normalX + point.normalY * point.normalY + point.normalZ * point.normalZ);
631 const float up = length > 0.f ? std::clamp(point.normalY / length, -1.f, 1.f) : 1.f;
632 const float slope = std::acos(up) * radiansToDegrees;
633 if (slope >= minDegrees && slope <= maxDegrees) appendPointRow(output, input, index);
634 }
635 return output;
636}
637
638PointSet filterPointsByPolygon(const PointSet& input, const PointSet& polygon, bool invert) {
640 if (polygon.points().size() < 3) return output;
641 for (size_t index = 0; index < input.points().size(); ++index) {
642 const auto& point = input.points()[index];
643 const bool inside = pointInPolygon(point.x, point.z, polygon.points());
644 if (inside != invert) appendPointRow(output, input, index);
645 }
646 return output;
647}
648
649PointSet filterPointsBySplineDistance(const PointSet& input, const PointSet& controlPoints, float minDistance,
650 float maxDistance) {
652 if (controlPoints.points().size() < 2) return output;
653 if (minDistance > maxDistance) std::swap(minDistance, maxDistance);
654 minDistance = std::max(0.f, minDistance);
655 maxDistance = std::max(0.f, maxDistance);
656 const float minSquared = minDistance * minDistance;
657 const float maxSquared = maxDistance * maxDistance;
658 for (size_t index = 0; index < input.points().size(); ++index) {
659 const auto& point = input.points()[index];
660 float nearest = std::numeric_limits<float>::max();
661 for (size_t i = 1; i < controlPoints.points().size(); ++i) {
662 nearest = std::min(nearest, pointSegmentDistanceSquared(point.x, point.z, controlPoints.points()[i - 1],
663 controlPoints.points()[i]));
664 }
665 if (nearest >= minSquared && nearest <= maxSquared) appendPointRow(output, input, index);
666 }
667 return output;
668}
669
670PointSet excludePointRadius(const PointSet& input, float x, float z, float radius) {
672 const float radiusSquared = std::max(0.f, radius) * std::max(0.f, radius);
673 for (size_t index = 0; index < input.points().size(); ++index) {
674 const auto& point = input.points()[index];
675 const float dx = point.x - x;
676 const float dz = point.z - z;
677 if (dx * dx + dz * dz > radiusSquared) appendPointRow(output, input, index);
678 }
679 return output;
680}
681
682Result<PointSet> excludePointsByGridMask(const PointSet& input, const Grid2D& mask, float originX, float originZ,
683 float cellSize, int semantic, float clearance, std::size_t maximumChecks) {
684 if (mask.getWidth() <= 0 || mask.getHeight() <= 0 || !std::isfinite(originX) || !std::isfinite(originZ) ||
685 !std::isfinite(cellSize) || cellSize <= 0.f || semantic < 0 || !std::isfinite(clearance) || clearance < 0.f ||
686 maximumChecks == 0u)
688 "grid point exclusion settings are invalid", "mask"));
689 const float radiusCellsFloat = clearance / cellSize;
690 if (radiusCellsFloat > 256.f)
692 "grid point exclusion clearance exceeds 256 cells",
693 "clearance"));
694 const int radiusCells = static_cast<int>(std::ceil(radiusCellsFloat));
695 const float radiusSquared = radiusCellsFloat * radiusCellsFloat;
696 std::size_t checks = 0u;
698 output.reserve(input.points().size());
699 for (std::size_t index = 0; index < input.points().size(); ++index) {
700 const auto& point = input.points()[index];
701 const int gridX = static_cast<int>(std::floor((point.x - originX) / cellSize));
702 const int gridZ = static_cast<int>(std::floor((point.z - originZ) / cellSize));
703 bool excluded = false;
704 for (int dz = -radiusCells; dz <= radiusCells && !excluded; ++dz) {
705 for (int dx = -radiusCells; dx <= radiusCells; ++dx) {
706 if (radiusCells > 0 && static_cast<float>(dx * dx + dz * dz) > radiusSquared) continue;
707 const int x = gridX + dx, z = gridZ + dz;
708 if (x < 0 || z < 0 || x >= mask.getWidth() || z >= mask.getHeight()) continue;
709 if (++checks > maximumChecks)
711 DiagnosticCode::PreconditionViolation, "grid point exclusion exceeds the work budget",
712 "maximumChecks"));
713 if (mask.getCell(x, z) == semantic) {
714 excluded = true;
715 break;
716 }
717 }
718 }
719 if (!excluded) appendPointRow(output, input, index);
720 }
721 return Result<PointSet>::success(std::move(output));
722}
723
724PointSet jitterPointPositions(const PointSet& input, uint32_t seed, float amountX, float amountZ) {
726 amountX = std::max(0.f, amountX);
727 amountZ = std::max(0.f, amountZ);
728 for (size_t i = 0; i < output.points().size(); ++i) {
729 auto& point = output.mutablePoint(i);
730 const uint32_t branchSeed = mix32(seed ^ point.seed ^ uint32_t(i));
731 point.x += (unitFloat(branchSeed) * 2.f - 1.f) * amountX;
732 point.z += (unitFloat(branchSeed ^ 0x63d83595u) * 2.f - 1.f) * amountZ;
733 }
734 return output;
735}
736
738 if (radius <= 0.f) return input;
740 const float radiusSquared = radius * radius;
741 for (size_t index = 0; index < input.points().size(); ++index) {
742 const auto& candidate = input.points()[index];
743 bool keep = true;
744 for (const auto& accepted : output.points()) {
745 const float dx = candidate.x - accepted.x;
746 const float dz = candidate.z - accepted.z;
747 if (dx * dx + dz * dz < radiusSquared) {
748 keep = false;
749 break;
750 }
751 }
752 if (keep) appendPointRow(output, input, index);
753 }
754 return output;
755}
756
757PointSet samplePolylinePoints(const PointSet& controlPoints, float spacing, uint32_t seed, float lateralJitter) {
759 if (controlPoints.points().size() < 2 || spacing <= 0.f) return output;
760 lateralJitter = std::max(0.f, lateralJitter);
761 float distanceToNext = 0.f;
762 uint32_t sampleIndex = 0;
763 for (size_t segment = 1; segment < controlPoints.points().size(); ++segment) {
764 const auto& a = controlPoints.points()[segment - 1];
765 const auto& b = controlPoints.points()[segment];
766 const float dx = b.x - a.x;
767 const float dy = b.y - a.y;
768 const float dz = b.z - a.z;
769 const float length = std::sqrt(dx * dx + dy * dy + dz * dz);
770 if (length <= 0.f) continue;
771 const float nx = -dz / length;
772 const float nz = dx / length;
773 const float yaw = std::atan2(dz, dx) * 57.29577951308232f;
774 while (distanceToNext <= length) {
775 const float t = distanceToNext / length;
776 const uint32_t sampleSeed = mix32(seed ^ sampleIndex);
777 const float lateral = (unitFloat(sampleSeed) * 2.f - 1.f) * lateralJitter;
779 point.x = a.x + dx * t + nx * lateral;
780 point.y = a.y + dy * t;
781 point.z = a.z + dz * t + nz * lateral;
782 point.yaw = yaw;
783 point.seed = sampleSeed ? sampleSeed : 1u;
784 point.id = derivePointId((std::uint64_t(seed) << 32u) | 0x504c494eu, sampleIndex);
785 const int appended = output.appendPoint(std::move(point));
786 (void)appended;
787 ++sampleIndex;
788 distanceToNext += spacing;
789 }
790 distanceToNext -= length;
791 }
792 return output;
793}
794
797 output.reserve(first.points().size() + second.points().size());
798 for (size_t index = 0; index < first.points().size(); ++index) appendPointRow(output, first, index);
799 for (size_t index = 0; index < second.points().size(); ++index) appendPointRow(output, second, index);
800 return output;
801}
802
803namespace {
804
805struct PointIdentity {
806 std::uint64_t id = 0;
807 std::uint32_t seed = 0;
808 float x = 0.f, y = 0.f, z = 0.f;
809
810 bool operator==(const PointIdentity& other) const noexcept {
811 if (id != 0 || other.id != 0) return id != 0 && id == other.id;
812 return seed == other.seed && x == other.x && y == other.y && z == other.z;
813 }
814};
815
816struct PointIdentityHash {
817 std::size_t operator()(const PointIdentity& value) const noexcept {
818 if (value.id != 0) return std::hash<std::uint64_t>{}(value.id);
819 auto combine = [](std::size_t hash, std::uint32_t part) {
820 return hash ^ (std::size_t(part) + 0x9e3779b9u + (hash << 6u) + (hash >> 2u));
821 };
822 std::size_t hash = value.seed;
823 hash = combine(hash, value.x == 0.f ? 0u : std::bit_cast<std::uint32_t>(value.x));
824 hash = combine(hash, value.y == 0.f ? 0u : std::bit_cast<std::uint32_t>(value.y));
825 return combine(hash, value.z == 0.f ? 0u : std::bit_cast<std::uint32_t>(value.z));
826 }
827};
828
829PointIdentity identityOf(const ProcgenPoint& point) {
830 return {point.id, point.seed, point.x, point.y, point.z};
831}
832
833void rotateEuler(float& x, float& y, float& z, float pitchDegrees, float yawDegrees, float rollDegrees) {
834 constexpr float degreesToRadians = 0.017453292519943295f;
835 const float pitch = pitchDegrees * degreesToRadians;
836 const float yaw = yawDegrees * degreesToRadians;
837 const float roll = rollDegrees * degreesToRadians;
838 const float cp = std::cos(pitch), sp = std::sin(pitch);
839 const float cy = std::cos(yaw), sy = std::sin(yaw);
840 const float cr = std::cos(roll), sr = std::sin(roll);
841 const float rolledX = x * cr - y * sr;
842 const float rolledY = x * sr + y * cr;
843 const float pitchedY = rolledY * cp - z * sp;
844 const float pitchedZ = rolledY * sp + z * cp;
845 x = rolledX * cy - pitchedZ * sy;
846 y = pitchedY;
847 z = rolledX * sy + pitchedZ * cy;
848}
849
850} // namespace
851
853 PointSet result;
854 std::unordered_set<PointIdentity, PointIdentityHash> identities;
855 identities.reserve(first.points().size() + second.points().size());
856 result.reserve(first.points().size() + second.points().size());
857 for (size_t index = 0; index < first.points().size(); ++index)
858 if (identities.insert(identityOf(first.points()[index])).second) appendPointRow(result, first, index);
859 for (size_t index = 0; index < second.points().size(); ++index)
860 if (identities.insert(identityOf(second.points()[index])).second) appendPointRow(result, second, index);
861 return result;
862}
863
865 PointSet result;
866 std::unordered_set<PointIdentity, PointIdentityHash> identities;
867 identities.reserve(second.points().size());
868 for (const auto& point : second.points()) identities.insert(identityOf(point));
869 result.reserve(std::min(first.points().size(), second.points().size()));
870 for (size_t index = 0; index < first.points().size(); ++index)
871 if (identities.contains(identityOf(first.points()[index]))) appendPointRow(result, first, index);
872 return result;
873}
874
876 PointSet result;
877 std::unordered_set<PointIdentity, PointIdentityHash> identities;
878 identities.reserve(second.points().size());
879 for (const auto& point : second.points()) identities.insert(identityOf(point));
880 result.reserve(first.points().size());
881 for (size_t index = 0; index < first.points().size(); ++index)
882 if (!identities.contains(identityOf(first.points()[index]))) appendPointRow(result, first, index);
883 return result;
884}
885
886PointSet transformPointSet(const PointSet& input, float translateX, float translateY, float translateZ,
887 float yawDegrees, float scaleX, float scaleY, float scaleZ) {
888 return transformPointSet3D(input, translateX, translateY, translateZ, 0.f, yawDegrees, 0.f, scaleX, scaleY, scaleZ);
889}
890
891PointSet transformPointSet3D(const PointSet& input, float translateX, float translateY, float translateZ,
892 float pitchDegrees, float yawDegrees, float rollDegrees, float scaleX, float scaleY,
893 float scaleZ) {
895 for (size_t index = 0; index < output.points().size(); ++index) {
897 float x = point.x * scaleX;
898 float y = point.y * scaleY;
899 float z = point.z * scaleZ;
900 rotateEuler(x, y, z, pitchDegrees, yawDegrees, rollDegrees);
901 point.x = x + translateX;
902 point.y = y + translateY;
903 point.z = z + translateZ;
904 point.pitch += pitchDegrees;
905 point.yaw += yawDegrees;
906 point.roll += rollDegrees;
907 point.scaleX *= scaleX;
908 point.scaleY *= scaleY;
909 point.scaleZ *= scaleZ;
910
911 point.normalX = point.normalX / (std::abs(scaleX) > 0.000001f ? scaleX : 1.f);
912 point.normalY = point.normalY / (std::abs(scaleY) > 0.000001f ? scaleY : 1.f);
913 point.normalZ = point.normalZ / (std::abs(scaleZ) > 0.000001f ? scaleZ : 1.f);
914 rotateEuler(point.normalX, point.normalY, point.normalZ, pitchDegrees, yawDegrees, rollDegrees);
915 const float normalLength =
916 std::sqrt(point.normalX * point.normalX + point.normalY * point.normalY + point.normalZ * point.normalZ);
917 if (normalLength > 0.f) {
918 point.normalX /= normalLength;
919 point.normalY /= normalLength;
920 point.normalZ /= normalLength;
921 }
922 }
923 return output;
924}
925
926PointSet copyPointsToTargets(const PointSet& source, const PointSet& targets, bool inheritTargetAttributes) {
927 constexpr float degreesToRadians = 0.017453292519943295f;
928 PointSet result;
929 if (!targets.points().empty() &&
930 source.points().size() > std::numeric_limits<size_t>::max() / targets.points().size())
931 return result;
932 result.reserve(source.points().size() * targets.points().size());
933 for (size_t targetIndex = 0; targetIndex < targets.points().size(); ++targetIndex) {
934 const auto& target = targets.points()[targetIndex];
935 const float radians = target.yaw * degreesToRadians;
936 const float cosine = std::cos(radians);
937 const float sine = std::sin(radians);
938 for (size_t sourceIndex = 0; sourceIndex < source.points().size(); ++sourceIndex) {
939 const auto& sourcePoint = source.points()[sourceIndex];
940 ProcgenPoint copy = sourcePoint;
941 const float localX = sourcePoint.x * target.scaleX;
942 const float localY = sourcePoint.y * target.scaleY;
943 const float localZ = sourcePoint.z * target.scaleZ;
944 copy.x = target.x + localX * cosine - localZ * sine;
945 copy.y = target.y + localY;
946 copy.z = target.z + localX * sine + localZ * cosine;
947 const float normalX = sourcePoint.normalX * cosine - sourcePoint.normalZ * sine;
948 const float normalZ = sourcePoint.normalX * sine + sourcePoint.normalZ * cosine;
949 copy.normalX = normalX;
950 copy.normalZ = normalZ;
951 copy.yaw += target.yaw;
952 copy.scaleX *= target.scaleX;
953 copy.scaleY *= target.scaleY;
954 copy.scaleZ *= target.scaleZ;
955 copy.density *= target.density;
956 copy.seed = deriveSeed(target.seed, "copy:" + std::to_string(sourcePoint.seed));
957 const std::uint64_t targetIdentity = target.id != 0 ? target.id : std::uint64_t(target.seed);
958 const std::uint64_t sourceIdentity = sourcePoint.id != 0 ? sourcePoint.id : std::uint64_t(sourcePoint.seed);
959 copy.id = derivePointId(targetIdentity, sourceIdentity);
960 const int resultIndex = result.appendPoint(std::move(copy));
961 if (inheritTargetAttributes) {
962 for (size_t column = 0; column < targets.attributes().columnCount(); ++column) {
963 const std::string name(targets.attributes().columnName(column));
964 if (!targets.attributes().has(targetIndex, name)) continue;
965 switch (*targets.attributes().typeOf(name)) {
967 result
968 .trySetFloatAttribute(resultIndex, name,
969 *targets.attributes().getFloat(targetIndex, name))
970 .expect("copy target float attribute");
971 break;
973 result
974 .trySetIntAttribute(resultIndex, name, *targets.attributes().getInt(targetIndex, name))
975 .expect("copy target integer attribute");
976 break;
978 result
979 .trySetBoolAttribute(resultIndex, name,
980 *targets.attributes().getBool(targetIndex, name))
981 .expect("copy target Boolean attribute");
982 break;
984 const auto value = *targets.attributes().getVector(targetIndex, name);
985 result.trySetVectorAttribute(resultIndex, name, value.x, value.y, value.z)
986 .expect("copy target vector attribute");
987 break;
988 }
990 result
991 .trySetStringAttribute(resultIndex, name,
992 std::string(*targets.attributes().getString(targetIndex, name)))
993 .expect("copy target string attribute");
994 break;
995 }
996 }
997 }
998 for (size_t column = 0; column < source.attributes().columnCount(); ++column) {
999 const std::string name(source.attributes().columnName(column));
1000 if (!source.attributes().has(sourceIndex, name)) continue;
1001 switch (*source.attributes().typeOf(name)) {
1003 result.trySetFloatAttribute(resultIndex, name, *source.attributes().getFloat(sourceIndex, name))
1004 .expect("copy source float attribute");
1005 break;
1007 result.trySetIntAttribute(resultIndex, name, *source.attributes().getInt(sourceIndex, name))
1008 .expect("copy source integer attribute");
1009 break;
1011 result.trySetBoolAttribute(resultIndex, name, *source.attributes().getBool(sourceIndex, name))
1012 .expect("copy source Boolean attribute");
1013 break;
1015 const auto value = *source.attributes().getVector(sourceIndex, name);
1016 result.trySetVectorAttribute(resultIndex, name, value.x, value.y, value.z)
1017 .expect("copy source vector attribute");
1018 break;
1019 }
1021 result
1022 .trySetStringAttribute(resultIndex, name,
1023 std::string(*source.attributes().getString(sourceIndex, name)))
1024 .expect("copy source string attribute");
1025 break;
1026 }
1027 }
1028 }
1029 }
1030 return result;
1031}
1032
1033PointSet remapPointDensity(const PointSet& input, float inputMin, float inputMax, float outputMin, float outputMax,
1034 bool clampOutput) {
1035 PointSet result = input;
1036 const float inputRange = inputMax - inputMin;
1037 for (size_t index = 0; index < result.points().size(); ++index) {
1038 auto& point = result.mutablePoint(index);
1039 float normalized = (point.density - inputMin) / inputRange;
1040 if (clampOutput) normalized = std::clamp(normalized, 0.f, 1.f);
1041 point.density = outputMin + normalized * (outputMax - outputMin);
1042 }
1043 return result;
1044}
1045
1046
1047PointSet filterPointStringAttribute(const PointSet& input, const std::string& name, const std::string& value,
1048 bool invert) {
1050 for (size_t index = 0; index < input.points().size(); ++index) {
1051 const auto found = input.attributes().getString(index, name);
1052 const bool matches = found && *found == value;
1053 if (matches != invert) appendPointRow(output, input, index);
1054 }
1055 return output;
1056}
1057
1058PointSet densityCullPoints(const PointSet& input, uint32_t seed, float multiplier) {
1060 multiplier = std::max(0.f, multiplier);
1061 for (size_t i = 0; i < input.points().size(); ++i) {
1062 const auto& point = input.points()[i];
1063 const uint32_t branchSeed = mix32(seed ^ point.seed ^ uint32_t(i));
1064 const float chance = std::clamp(point.density * multiplier, 0.f, 1.f);
1065 if (unitFloat(branchSeed) < chance) appendPointRow(output, input, i);
1066 }
1067 return output;
1068}
1069
1070PointSet densityFromNormal(const PointSet& input, float minDegrees, float maxDegrees, float outputMin, float outputMax,
1071 bool invert) {
1072 if (minDegrees > maxDegrees) std::swap(minDegrees, maxDegrees);
1073 PointSet result = input;
1074 const float range = std::max(0.000001f, maxDegrees - minDegrees);
1075 constexpr float radToDeg = 57.29577951308232f;
1076 for (size_t index = 0; index < result.points().size(); ++index) {
1077 auto& point = result.mutablePoint(index);
1078 const float ny = std::clamp(point.normalY, -1.f, 1.f);
1079 const float degrees = std::acos(ny) * radToDeg;
1080 float t = (degrees - minDegrees) / range;
1081 t = std::clamp(t, 0.f, 1.f);
1082 if (invert) t = 1.f - t;
1083 point.density = outputMin + t * (outputMax - outputMin);
1084 point.steepness = std::clamp(degrees / 90.f, 0.f, 1.f);
1085 }
1086 return result;
1087}
1088
1089PointSet modifyPointBounds(const PointSet& input, float scaleX, float scaleY, float scaleZ, float padX, float padY,
1090 float padZ) {
1091 PointSet result = input;
1092 padX = std::max(0.f, padX);
1093 padY = std::max(0.f, padY);
1094 padZ = std::max(0.f, padZ);
1095 for (size_t index = 0; index < result.points().size(); ++index) {
1096 auto& point = result.mutablePoint(index);
1097 const float cx = 0.5f * (point.boundsMinX + point.boundsMaxX);
1098 const float cy = 0.5f * (point.boundsMinY + point.boundsMaxY);
1099 const float cz = 0.5f * (point.boundsMinZ + point.boundsMaxZ);
1100 float hx = 0.5f * (point.boundsMaxX - point.boundsMinX);
1101 float hy = 0.5f * (point.boundsMaxY - point.boundsMinY);
1102 float hz = 0.5f * (point.boundsMaxZ - point.boundsMinZ);
1103 if (hx <= 0.f) hx = 0.5f * std::abs(scaleX);
1104 else
1105 hx *= scaleX;
1106 if (hy <= 0.f) hy = 0.5f * std::abs(scaleY);
1107 else
1108 hy *= scaleY;
1109 if (hz <= 0.f) hz = 0.5f * std::abs(scaleZ);
1110 else
1111 hz *= scaleZ;
1112 point.boundsMinX = cx - hx - padX;
1113 point.boundsMaxX = cx + hx + padX;
1114 point.boundsMinY = cy - hy - padY;
1115 point.boundsMaxY = cy + hy + padY;
1116 point.boundsMinZ = cz - hz - padZ;
1117 point.boundsMaxZ = cz + hz + padZ;
1118 }
1119 return result;
1120}
1121
1122PointSet assignWeightedMeshAttribute(const PointSet& input, uint32_t seed, const std::string& attribute,
1123 const std::string* meshes, const float* weights, int entryCount) {
1124 PointSet result = input;
1125 if (attribute.empty() || !meshes || !weights || entryCount <= 0) return result;
1126 float total = 0.f;
1127 for (int entry = 0; entry < entryCount; ++entry)
1128 if (!meshes[entry].empty() && weights[entry] > 0.f) total += weights[entry];
1129 if (total <= 0.f) return result;
1130 for (size_t index = 0; index < result.points().size(); ++index) {
1131 const auto& point = result.points()[index];
1132 const uint32_t branchSeed = mix32(seed ^ point.seed ^ uint32_t(index) ^ 0x6d657368u);
1133 float cursor = unitFloat(branchSeed) * total;
1134 std::string chosen;
1135 for (int entry = 0; entry < entryCount; ++entry) {
1136 if (meshes[entry].empty() || weights[entry] <= 0.f) continue;
1137 cursor -= weights[entry];
1138 if (cursor <= 0.f) {
1139 chosen = meshes[entry];
1140 break;
1141 }
1142 }
1143 if (chosen.empty()) {
1144 for (int entry = entryCount - 1; entry >= 0; --entry)
1145 if (!meshes[entry].empty() && weights[entry] > 0.f) {
1146 chosen = meshes[entry];
1147 break;
1148 }
1149 }
1150 if (!chosen.empty())
1151 result.trySetStringAttribute(int(index), attribute, chosen)
1152 .expect("assignWeightedMeshAttribute schema");
1153 }
1154 return result;
1155}
1156
1157} // namespace eve::procgen
LogicalId target
double value
bool & active
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
#define EV_ASSERT(cond,...)
Assert an internal engine invariant (state that must always hold).
Definition Assert.h:37
#define EV_PARAM_CHECK(cond,...)
Validate a function parameter / public API precondition.
Definition Assert.h:30
std::string output
std::string from
int mask
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
float degrees
Definition CardTypes.cpp:35
float length
Definition CaveMesh.cpp:94
float nx
float nz
float ny
float pz
glm::vec4 p[6]
Stable, structured diagnostics shared by engine modules.
int column
std::array< std::uint8_t, 32 > hash
Definition Evpack.cpp:172
EvpackChunkInput input
Definition Evpack.cpp:170
float u
Definition Grass.cpp:233
HexVec3 up
std::int32_t second
std::int32_t first
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
std::vector< Colorf > px
std::uint32_t width
JobScope scope
Range range
std::int32_t parent
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
graphics::Canvas * previous
int idx
float radius
std::string id
Definition PlayHost.cpp:108
std::uint32_t seed
Definition PointSet.cpp:807
float t
bool found
float dz
float dy
float dx
bool occupied
std::uint32_t count
Cell cell
TacticalUnit * unit
std::size_t cursor
float weights[3]
int spacing
float size
Definition TreeMesh.cpp:156
uint32_t index
const UnitySourceAsset & source
std::uint32_t depth
double oy
std::vector< char > inside
double ox
glm::vec3 point
uint32_t semantic
Definition WfcSimple.cpp:15
float vz
float vx
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
void expect(std::string_view message) const
Require success for a void operation.
Definition Result.h:548
void ignore(std::string_view reason={}) const noexcept
Explicitly discard this result after documenting the reason.
Definition Result.h:537
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
std::optional< std::string_view > getString(std::size_t row, std::string_view name) const
Borrow a string value until the next table mutation, or return no value.
Result< void > copyColumn(std::string_view from, std::string_view to)
Copy one typed column onto another name, creating or overwriting a compatible target.
std::optional< std::int64_t > getInt(std::size_t row, std::string_view name) const
Read an integer value, or no value for an absent row, name, or type.
bool has(std::size_t row, std::string_view name) const
Return whether a row contains a value in the named column.
std::optional< ProcgenAttributeVector > getVector(std::size_t row, std::string_view name) const
Read a vector value, or no value for an absent row, name, or type.
std::size_t appendRow()
Append one empty row and return its stable row index.
std::optional< bool > getBool(std::size_t row, std::string_view name) const
Read a Boolean value, or no value for an absent row, name, or type.
Result< void > removeColumn(std::string_view name)
Remove one metadata column and its values from every row.
Result< void > setBool(std::size_t row, std::string_view name, bool value)
Set a Boolean value, rejecting an incompatible existing schema.
void clear() noexcept
Remove all rows and schema columns.
Result< void > setInt(std::size_t row, std::string_view name, std::int64_t value)
Set an integer value, rejecting an incompatible existing schema.
Result< void > setFloat(std::size_t row, std::string_view name, float value)
Set a float value, rejecting an incompatible existing schema.
Result< std::size_t > appendRowFrom(const AttributeTable &source, std::size_t sourceRow)
Append one row copied from another table, merging compatible schema columns.
std::string_view columnName(std::size_t index) const noexcept
Return a stable insertion-ordered column name or an empty string.
Result< void > setVector(std::size_t row, std::string_view name, ProcgenAttributeVector value)
Set a vector value, rejecting an incompatible existing schema.
Result< void > renameColumn(std::string_view from, std::string_view to)
Rename one metadata column without changing values or type.
Result< void > setString(std::size_t row, std::string_view name, std::string value)
Set a string value, rejecting an incompatible existing schema.
void resize(std::size_t rows)
Resize every column to the same row count.
Result< void > clearRow(std::size_t row)
Clear every value in one row while preserving the table schema.
std::optional< float > getFloat(std::size_t row, std::string_view name) const
Read a float value, or no value for an absent row, name, or type.
std::optional< ProcgenAttributeType > typeOf(std::string_view name) const
Return the declared type for a name, or no value when absent.
Intermediate 2D generation result. cells store semantic ids (see Semantic.h), not tile GIDs — convert...
Definition Grid2D.h:36
Script-friendly collection of attributed 3D samples.
Definition PointSet.h:59
std::string getAttributeType(int index, const std::string &name) const
Return float, int, bool, vector, string, or empty when the attribute is absent.
Definition PointSet.cpp:453
Result< void > trySetPointId(int index, std::uint64_t id)
Assign a unique non-zero point identity without mutating on failure.
Definition PointSet.cpp:294
void setFloatAttribute(int index, const std::string &name, float value)
Compatibility-only unchecked script setter; canonical code uses trySetFloatAttribute.
Definition PointSet.cpp:338
float getBoundsMinX(int index) const
Return the local-space minimum X bound.
Definition PointSet.cpp:213
bool hasVectorAttribute(int index, const std::string &name) const
Test whether vector metadata exists.
Definition PointSet.cpp:417
float getNormalZ(int index) const
Returns the normal z.
Definition PointSet.cpp:150
bool hasStringAttribute(int index, const std::string &name) const
True when string attribute.
Definition PointSet.cpp:437
bool empty() const
Empty.
Definition PointSet.cpp:49
bool hasIntAttribute(int index, const std::string &name) const
Test whether signed integer metadata exists.
Definition PointSet.cpp:367
float getColorR(int index) const
Return the point's linear red channel.
Definition PointSet.cpp:247
bool hasBoolAttribute(int index, const std::string &name) const
Test whether boolean metadata exists.
Definition PointSet.cpp:387
int add(float x, float y, float z)
Definition PointSet.cpp:97
float getFloatAttribute(int index, const std::string &name, float fallback) const
Returns the float attribute.
Definition PointSet.cpp:342
void setScale(int index, float x, float y, float z)
Sets the scale.
Definition PointSet.cpp:181
float getBoundsMaxZ(int index) const
Return the local-space maximum Z bound.
Definition PointSet.cpp:233
Result< void > tryCopyAttribute(const std::string &from, const std::string &to)
Copy one metadata column onto another name on this set.
Definition PointSet.cpp:449
float getBoundsMaxY(int index) const
Return the local-space maximum Y bound.
Definition PointSet.cpp:229
Result< void > clearPointAttributes(std::size_t index)
Clear all metadata values on an existing point while retaining the set schema.
Definition PointSet.cpp:85
float getBoundsMaxX(int index) const
Return the local-space maximum X bound.
Definition PointSet.cpp:225
Result< void > trySetStringAttribute(int index, const std::string &name, const std::string &value)
Canonical checked string metadata write.
Definition PointSet.cpp:421
float getColorG(int index) const
Return the point's linear green channel.
Definition PointSet.cpp:251
Result< void > tryRenameAttribute(const std::string &from, const std::string &to)
Rename one metadata column on this set.
Definition PointSet.cpp:441
std::string getStringAttribute(int index, const std::string &name, const std::string &fallback) const
Returns the string attribute.
Definition PointSet.cpp:432
void setColor(int index, float red, float green, float blue, float alpha)
Set the normalized linear RGBA point color.
Definition PointSet.cpp:238
float getY(int index) const
Returns the y.
Definition PointSet.cpp:125
float getScaleX(int index) const
Returns the scale x.
Definition PointSet.cpp:189
float getVectorAttributeY(int index, const std::string &name, float fallback) const
Read the Y component of vector metadata or the caller-provided default.
Definition PointSet.cpp:407
Result< void > trySetBoolAttribute(int index, const std::string &name, bool value)
Canonical checked Boolean metadata write.
Definition PointSet.cpp:371
float getYaw(int index) const
Returns the yaw.
Definition PointSet.cpp:158
void setIntAttribute(int index, const std::string &name, std::int64_t value)
Compatibility-only unchecked script setter; canonical code uses trySetIntAttribute.
Definition PointSet.cpp:358
float getBoundsMinY(int index) const
Return the local-space minimum Y bound.
Definition PointSet.cpp:217
ProcgenPoint & mutablePoint(std::size_t index)
Mutably access one existing point without changing row structure.
Definition PointSet.cpp:92
Result< void > trySetVectorAttribute(int index, const std::string &name, float x, float y, float z)
Canonical checked vector metadata write.
Definition PointSet.cpp:391
void setBounds(int index, float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Set local-space point bounds before scale and rotation are applied.
Definition PointSet.cpp:202
Result< void > assignPointIds(std::uint64_t namespaceId)
Fill zero identities deterministically and reject pre-existing duplicates.
Definition PointSet.cpp:310
bool hasFloatAttribute(int index, const std::string &name) const
True when float attribute.
Definition PointSet.cpp:347
float getPitch(int index) const
Return the point's local pitch in degrees.
Definition PointSet.cpp:171
void setBoolAttribute(int index, const std::string &name, bool value)
Compatibility-only unchecked script setter; canonical code uses trySetBoolAttribute.
Definition PointSet.cpp:378
std::uint64_t getPointId(int index) const
Return the stable point identity, or zero when it has not been assigned.
Definition PointSet.cpp:289
float getVectorAttributeZ(int index, const std::string &name, float fallback) const
Read the Z component of vector metadata or the caller-provided default.
Definition PointSet.cpp:412
void setVectorAttribute(int index, const std::string &name, float x, float y, float z)
Compatibility-only unchecked script setter; canonical code uses trySetVectorAttribute.
Definition PointSet.cpp:398
void setPosition(int index, float x, float y, float z)
Sets the position.
Definition PointSet.cpp:113
uint32_t getPointSeed(int index) const
Returns the point seed.
Definition PointSet.cpp:284
void setSteepness(int index, float steepness)
Set normalized surface steepness metadata in the inclusive range [0, 1].
Definition PointSet.cpp:264
float getX(int index) const
Returns the x.
Definition PointSet.cpp:121
float getNormalX(int index) const
Returns the normal x.
Definition PointSet.cpp:142
bool getBoolAttribute(int index, const std::string &name, bool fallback) const
Read boolean metadata or return the caller-provided default when absent.
Definition PointSet.cpp:382
int appendPoint(ProcgenPoint point)
Append a point with an empty attribute row and return its row index.
Definition PointSet.cpp:62
float getRoll(int index) const
Return the point's local roll in degrees.
Definition PointSet.cpp:176
void setYaw(int index, float yaw)
Sets the yaw.
Definition PointSet.cpp:155
float getScaleZ(int index) const
Returns the scale z.
Definition PointSet.cpp:197
float getZ(int index) const
Returns the z.
Definition PointSet.cpp:129
void setStringAttribute(int index, const std::string &name, const std::string &value)
Compatibility-only unchecked script setter; canonical code uses trySetStringAttribute.
Definition PointSet.cpp:428
void setPointSeed(int index, uint32_t seed)
Sets the point seed.
Definition PointSet.cpp:281
const std::vector< ProcgenPoint > & points() const
Borrow immutable point rows; structural ownership remains with this set.
Definition PointSet.h:212
float getNormalY(int index) const
Returns the normal y.
Definition PointSet.cpp:146
void setNormal(int index, float x, float y, float z)
Sets the normal.
Definition PointSet.cpp:134
float getVectorAttributeX(int index, const std::string &name, float fallback) const
Read the X component of vector metadata or the caller-provided default.
Definition PointSet.cpp:402
float getScaleY(int index) const
Returns the scale y.
Definition PointSet.cpp:193
void setDensity(int index, float density)
Sets the density.
Definition PointSet.cpp:273
float getColorA(int index) const
Return the point's linear alpha channel.
Definition PointSet.cpp:259
Result< void > trySetIntAttribute(int index, const std::string &name, std::int64_t value)
Canonical checked signed integer metadata write.
Definition PointSet.cpp:351
std::int64_t getIntAttribute(int index, const std::string &name, std::int64_t fallback) const
Read signed integer metadata or return the caller-provided default when absent.
Definition PointSet.cpp:362
int getCount() const
Returns the count.
Definition PointSet.cpp:48
void clear()
Clears .
Definition PointSet.cpp:50
float getSteepness(int index) const
Return normalized point steepness metadata.
Definition PointSet.cpp:268
Result< int > appendPointFrom(const PointSet &source, std::size_t sourceIndex)
Append a point and its attributes from another set.
Definition PointSet.cpp:69
void setRotation(int index, float pitch, float yaw, float roll)
Set the point's local Euler rotation in degrees.
Definition PointSet.cpp:163
const AttributeTable & attributes() const noexcept
Borrow the authoritative schema-bearing attribute table.
Definition PointSet.h:216
float getBoundsMinZ(int index) const
Return the local-space minimum Z bound.
Definition PointSet.cpp:221
float getDensity(int index) const
Returns the density.
Definition PointSet.cpp:277
float getColorB(int index) const
Return the point's linear blue channel.
Definition PointSet.cpp:255
Result< void > trySetFloatAttribute(int index, const std::string &name, float value)
Canonical checked float metadata write.
Definition PointSet.cpp:331
Result< void > tryDeleteAttribute(const std::string &name)
Delete one metadata column on this set.
Definition PointSet.cpp:445
void reserve(std::size_t count)
Reserve point storage without changing point or attribute row counts.
Definition PointSet.cpp:60
float length2(float x, float z)
Length 2.
Definition AnimMath.h:150
bool pointInPolygon(const Vec2 &p, const Polygon &poly)
Point-in-polygon test (ray casting; boundary counts as inside).
PointSet samplePolylinePoints(const PointSet &controlPoints, float spacing, uint32_t seed, float lateralJitter)
Sample polyline points.
Definition PointSet.cpp:757
PointSet transformPointSet3D(const PointSet &input, float translateX, float translateY, float translateZ, float pitchDegrees, float yawDegrees, float rollDegrees, float scaleX, float scaleY, float scaleZ)
Apply translation, pitch/yaw/roll rotation and non-uniform scale.
Definition PointSet.cpp:891
PointSet densityFromNormal(const PointSet &input, float minDegrees, float maxDegrees, float outputMin, float outputMax, bool invert)
Remap surface slope (from normals) into density.
PointSet filterPointStringAttribute(const PointSet &input, const std::string &name, const std::string &value, bool invert)
Select points whose named string attribute equals a value.
PointSet unionPointSets(const PointSet &first, const PointSet &second)
Stable union by non-zero point id, with legacy position-and-seed fallback.
Definition PointSet.cpp:852
PointSet transformPointSet(const PointSet &input, float translateX, float translateY, float translateZ, float yawDegrees, float scaleX, float scaleY, float scaleZ)
Apply translation, yaw rotation and non-uniform scale to points and their transforms.
Definition PointSet.cpp:886
PointSet filterPointsByPolygon(const PointSet &input, const PointSet &polygon, bool invert)
Filter points by polygon.
Definition PointSet.cpp:638
PointSet filterPointBox(const PointSet &input, float minX, float minY, float minZ, float maxX, float maxY, float maxZ, bool invert)
Filter point box.
Definition PointSet.cpp:606
PointSet filterPointSlope(const PointSet &input, float minDegrees, float maxDegrees)
Filter point slope.
Definition PointSet.cpp:621
PointSet assignWeightedMeshAttribute(const PointSet &input, uint32_t seed, const std::string &attribute, const std::string *meshes, const float *weights, int entryCount)
Deterministically assign a weighted mesh path string attribute.
PointSet poissonDiskPoints(int width, int depth, float radius, uint32_t seed, int maxPoints)
Bridson blue-noise (Poisson disk) samples in a width x depth area (XZ, y=0).
Definition PointSet.cpp:502
PointSet filterPointsBySplineDistance(const PointSet &input, const PointSet &controlPoints, float minDistance, float maxDistance)
Filter points by spline distance.
Definition PointSet.cpp:649
PointSet excludePointRadius(const PointSet &input, float x, float z, float radius)
Exclude point radius.
Definition PointSet.cpp:670
PointSet intersectPointSets(const PointSet &first, const PointSet &second)
Keep first-set points whose stable or legacy identity occurs in the second set.
Definition PointSet.cpp:864
void appendPointRow(PointSet &output, const PointSet &input, std::size_t index)
Definition PointSet.cpp:56
PointSet mergePointSets(const PointSet &first, const PointSet &second)
Concatenate two attributed point collections while preserving order.
Definition PointSet.cpp:795
PointSet copyPointsToTargets(const PointSet &source, const PointSet &targets, bool inheritTargetAttributes)
Instantiate source points relative to targets in stable target-major order.
Definition PointSet.cpp:926
PointSet filterPointDensity(const PointSet &input, float minDensity, float maxDensity)
Filter point density.
Definition PointSet.cpp:597
std::uint64_t derivePointId(std::uint64_t namespaceId, std::uint64_t ordinal)
Deterministically derive a non-zero stable point identity.
Definition PointSet.cpp:469
PointSet selfPrunePoints(const PointSet &input, float radius)
Self prune points.
Definition PointSet.cpp:737
std::string_view procgenAttributeTypeName(ProcgenAttributeType type) noexcept
Return the stable script/persistence name for an attribute type.
PointSet filterPointHeight(const PointSet &input, float minHeight, float maxHeight)
Filter point height.
Definition PointSet.cpp:588
PointSet modifyPointBounds(const PointSet &input, float scaleX, float scaleY, float scaleZ, float padX, float padY, float padZ)
Scale and pad each point's local bounds about its center.
PointSet densityCullPoints(const PointSet &input, uint32_t seed, float multiplier)
Deterministically keep points according to density and a root seed.
PointSet differencePointSets(const PointSet &first, const PointSet &second)
Remove first-set points whose stable or legacy identity occurs in the second set.
Definition PointSet.cpp:875
uint32_t deriveSeed(uint32_t parent, const std::string &scope)
Stable label-based seed derivation; independent pipeline branches do not perturb each other.
Definition PointSet.cpp:459
PointSet jitterPointPositions(const PointSet &input, uint32_t seed, float amountX, float amountZ)
Jitter point positions.
Definition PointSet.cpp:724
PointSet remapPointDensity(const PointSet &input, float inputMin, float inputMax, float outputMin, float outputMax, bool clampOutput)
Linearly remap point density between ranges with optional output clamping.
PointSet sampleGridPoints(int width, int depth, float spacing, uint32_t seed, float jitter)
Sample grid points.
Definition PointSet.cpp:480
Result< PointSet > excludePointsByGridMask(const PointSet &input, const Grid2D &mask, float originX, float originZ, float cellSize, int semantic, float clearance, std::size_t maximumChecks)
Remove points covered by a semantic grid mask plus optional world-space clearance.
Definition PointSet.cpp:682
One deterministic sample used by script-first procedural pipelines.
Definition PointSet.h:17
std::uint64_t id
Stable non-zero identity; zero marks legacy or not-yet-assigned data.
Definition PointSet.h:19