载入中...
搜索中...
未找到
SpatialData.cpp
浏览该文件的文档.
2
3#include "procgen/MeshBuild.h"
4
5#include <algorithm>
6#include <cmath>
7#include <limits>
8#include <string>
9#include <string_view>
10
11namespace eve::procgen {
12namespace {
13
14uint32_t mixSpatialSeed(uint32_t value) {
15 value += 0x9e3779b9u;
16 value = (value ^ (value >> 16u)) * 0x21f0aaadu;
17 value = (value ^ (value >> 15u)) * 0x735a2d97u;
18 return value ^ (value >> 15u);
19}
20
21float unitFloat(uint32_t seed) { return float(mixSpatialSeed(seed) >> 8u) * (1.f / 16777216.f); }
22
23SpatialBounds unionBounds(const SpatialBounds& a, const SpatialBounds& b) {
24 if (!a.valid) return b;
25 if (!b.valid) return a;
26 return {std::min(a.minX, b.minX),
27 std::min(a.minY, b.minY),
28 std::min(a.minZ, b.minZ),
29 std::max(a.maxX, b.maxX),
30 std::max(a.maxY, b.maxY),
31 std::max(a.maxZ, b.maxZ),
32 true};
33}
34
35SpatialBounds intersectionBounds(const SpatialBounds& a, const SpatialBounds& b) {
36 if (!a.valid || !b.valid) return {};
37 SpatialBounds result{std::max(a.minX, b.minX),
38 std::max(a.minY, b.minY),
39 std::max(a.minZ, b.minZ),
40 std::min(a.maxX, b.maxX),
41 std::min(a.maxY, b.maxY),
42 std::min(a.maxZ, b.maxZ),
43 true};
44 if (result.minX > result.maxX || result.minY > result.maxY || result.minZ > result.maxZ) return {};
45 return result;
46}
47
48float pointSegmentDistanceSquared(float px, float py, float pz, const ProcgenPoint& a, const ProcgenPoint& b) {
49 const float vx = b.x - a.x;
50 const float vy = b.y - a.y;
51 const float vz = b.z - a.z;
52 const float l2 = vx * vx + vy * vy + vz * vz;
53 const float t = l2 > 0.f ? std::clamp(((px - a.x) * vx + (py - a.y) * vy + (pz - a.z) * vz) / l2, 0.f, 1.f) : 0.f;
54 const float dx = px - (a.x + vx * t);
55 const float dy = py - (a.y + vy * t);
56 const float dz = pz - (a.z + vz * t);
57 return dx * dx + dy * dy + dz * dz;
58}
59
60bool pointInPolygon(float x, float z, const std::vector<ProcgenPoint>& polygon) {
61 bool inside = false;
62 for (size_t i = 0, previous = polygon.size() - 1; i < polygon.size(); previous = i++) {
63 const auto& a = polygon[i];
64 const auto& b = polygon[previous];
65 const bool crosses = ((a.z > z) != (b.z > z)) && (x < (b.x - a.x) * (z - a.z) / (b.z - a.z) + a.x);
66 if (crosses) inside = !inside;
67 }
68 return inside;
69}
70
71bool triangleHeightAtXZ(const std::vector<float>& positions, std::uint32_t ia, std::uint32_t ib, std::uint32_t ic,
72 float x, float z, float& y, float& nx, float& ny, float& nz) {
73 const size_t a = size_t(ia) * 3;
74 const size_t b = size_t(ib) * 3;
75 const size_t c = size_t(ic) * 3;
76 if (a + 2 >= positions.size() || b + 2 >= positions.size() || c + 2 >= positions.size()) return false;
77 const float ax = positions[a], ay = positions[a + 1], az = positions[a + 2];
78 const float bx = positions[b], by = positions[b + 1], bz = positions[b + 2];
79 const float cx = positions[c], cy = positions[c + 1], cz = positions[c + 2];
80 const float denominator = (bz - cz) * (ax - cx) + (cx - bx) * (az - cz);
81 if (std::abs(denominator) <= 1e-8f) return false;
82 const float wa = ((bz - cz) * (x - cx) + (cx - bx) * (z - cz)) / denominator;
83 const float wb = ((cz - az) * (x - cx) + (ax - cx) * (z - cz)) / denominator;
84 const float wc = 1.f - wa - wb;
85 constexpr float epsilon = 1e-5f;
86 if (wa < -epsilon || wb < -epsilon || wc < -epsilon) return false;
87 y = wa * ay + wb * by + wc * cy;
88 const float ux = bx - ax, uy = by - ay, uz = bz - az;
89 const float vx = cx - ax, vy = cy - ay, vz = cz - az;
90 nx = uy * vz - uz * vy;
91 ny = uz * vx - ux * vz;
92 nz = ux * vy - uy * vx;
93 const float length = std::sqrt(nx * nx + ny * ny + nz * nz);
94 if (length > 0.f) {
95 nx /= length;
96 ny /= length;
97 nz /= length;
98 }
99 if (ny < 0.f) {
100 nx = -nx;
101 ny = -ny;
102 nz = -nz;
103 }
104 return true;
105}
106
107bool closestMeshHeight(const MeshBuild& mesh, float x, float z, float referenceY, float& y, float& nx, float& ny,
108 float& nz) {
109 bool found = false;
110 float bestDistance = std::numeric_limits<float>::max();
111 const auto& positions = mesh.positions();
112 const auto& indices = mesh.indices();
113 for (size_t i = 0; i + 2 < indices.size(); i += 3) {
114 float candidateY = 0.f, candidateNx = 0.f, candidateNy = 1.f, candidateNz = 0.f;
115 if (!triangleHeightAtXZ(positions, indices[i], indices[i + 1], indices[i + 2], x, z, candidateY, candidateNx,
116 candidateNy, candidateNz))
117 continue;
118 const float distance = std::abs(referenceY - candidateY);
119 if (distance >= bestDistance) continue;
120 found = true;
121 bestDistance = distance;
122 y = candidateY;
123 nx = candidateNx;
124 ny = candidateNy;
125 nz = candidateNz;
126 }
127 return found;
128}
129
130} // namespace
131
133 SpatialData result;
134 if (points.empty()) return result;
135 result.kind_ = Kind::Points;
136 result.points_ = points;
137 auto& bounds = result.bounds_;
138 bounds.minX = bounds.minY = bounds.minZ = std::numeric_limits<float>::max();
139 bounds.maxX = bounds.maxY = bounds.maxZ = std::numeric_limits<float>::lowest();
140 for (const auto& point : points.points()) {
141 bounds.minX = std::min(bounds.minX, point.x);
142 bounds.minY = std::min(bounds.minY, point.y);
143 bounds.minZ = std::min(bounds.minZ, point.z);
144 bounds.maxX = std::max(bounds.maxX, point.x);
145 bounds.maxY = std::max(bounds.maxY, point.y);
146 bounds.maxZ = std::max(bounds.maxZ, point.z);
147 }
148 bounds.valid = true;
149 return result;
150}
151
152SpatialData SpatialData::box(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
153 SpatialData result;
154 result.kind_ = Kind::Box;
155 result.bounds_ = {std::min(minX, maxX),
156 std::min(minY, maxY),
157 std::min(minZ, maxZ),
158 std::max(minX, maxX),
159 std::max(minY, maxY),
160 std::max(minZ, maxZ),
161 true};
162 return result;
163}
164
165SpatialData SpatialData::sphere(float x, float y, float z, float radius) {
166 SpatialData result;
167 if (radius <= 0.f) return result;
168 result.kind_ = Kind::Sphere;
169 result.centerX_ = x;
170 result.centerY_ = y;
171 result.centerZ_ = z;
172 result.radius_ = radius;
173 result.bounds_ = {x - radius, y - radius, z - radius, x + radius, y + radius, z + radius, true};
174 return result;
175}
176
177SpatialData SpatialData::polygon(const PointSet& controlPoints, float minY, float maxY) {
178 SpatialData result = fromPoints(controlPoints);
179 if (controlPoints.getCount() < 3) return {};
180 result.kind_ = Kind::Polygon;
181 result.bounds_.minY = std::min(minY, maxY);
182 result.bounds_.maxY = std::max(minY, maxY);
183 return result;
184}
185
186SpatialData SpatialData::spline(const PointSet& controlPoints, float radius) {
187 SpatialData result = fromPoints(controlPoints);
188 if (controlPoints.getCount() < 2 || radius < 0.f) return {};
189 result.kind_ = Kind::Spline;
190 result.radius_ = radius;
191 result.bounds_.minX -= radius;
192 result.bounds_.minY -= radius;
193 result.bounds_.minZ -= radius;
194 result.bounds_.maxX += radius;
195 result.bounds_.maxY += radius;
196 result.bounds_.maxZ += radius;
197 return result;
198}
199
200SpatialData SpatialData::heightfield(const Heightmap& heightmap, float originX, float originZ, float cellSize,
201 float heightScale) {
202 SpatialData result;
203 if (heightmap.getWidth() <= 0 || heightmap.getHeight() <= 0 || cellSize <= 0.f) return result;
204 result.kind_ = Kind::Heightfield;
205 result.heightmap_ = std::make_shared<Heightmap>(heightmap);
206 result.originX_ = originX;
207 result.originZ_ = originZ;
208 result.cellSize_ = cellSize;
209 result.heightScale_ = heightScale;
210 float minHeight = std::numeric_limits<float>::max();
211 float maxHeight = std::numeric_limits<float>::lowest();
212 for (int z = 0; z < heightmap.getHeight(); ++z) {
213 for (int x = 0; x < heightmap.getWidth(); ++x) {
214 const float value = heightmap.height(x, z) * heightScale;
215 minHeight = std::min(minHeight, value);
216 maxHeight = std::max(maxHeight, value);
217 }
218 }
219 result.bounds_ = {originX, minHeight,
220 originZ, originX + float(heightmap.getWidth() - 1) * cellSize,
221 maxHeight, originZ + float(heightmap.getHeight() - 1) * cellSize,
222 true};
223 return result;
224}
225
226SpatialData SpatialData::textureMask(const Heightmap& values, float originX, float originZ, float cellSize,
227 float minValue, float maxValue, float minY, float maxY) {
228 SpatialData result;
229 if (values.getWidth() <= 0 || values.getHeight() <= 0 || cellSize <= 0.f) return result;
230 result.kind_ = Kind::TextureMask;
231 result.heightmap_ = std::make_shared<Heightmap>(values);
232 result.originX_ = originX;
233 result.originZ_ = originZ;
234 result.cellSize_ = cellSize;
235 result.minValue_ = std::min(minValue, maxValue);
236 result.maxValue_ = std::max(minValue, maxValue);
237 result.bounds_ = {originX,
238 std::min(minY, maxY),
239 originZ,
240 originX + float(values.getWidth() - 1) * cellSize,
241 std::max(minY, maxY),
242 originZ + float(values.getHeight() - 1) * cellSize,
243 true};
244 return result;
245}
246
248 SpatialData result;
249 if (mesh.positions().size() < 9 || mesh.indices().size() < 3 || tolerance < 0.f) return result;
250 result.kind_ = Kind::MeshSurface;
251 result.mesh_ = std::make_shared<MeshBuild>(mesh);
252 result.radius_ = tolerance;
253 auto& bounds = result.bounds_;
254 bounds.minX = bounds.minY = bounds.minZ = std::numeric_limits<float>::max();
255 bounds.maxX = bounds.maxY = bounds.maxZ = std::numeric_limits<float>::lowest();
256 for (size_t i = 0; i + 2 < mesh.positions().size(); i += 3) {
257 bounds.minX = std::min(bounds.minX, mesh.positions()[i]);
258 bounds.minY = std::min(bounds.minY, mesh.positions()[i + 1]);
259 bounds.minZ = std::min(bounds.minZ, mesh.positions()[i + 2]);
260 bounds.maxX = std::max(bounds.maxX, mesh.positions()[i]);
261 bounds.maxY = std::max(bounds.maxY, mesh.positions()[i + 1]);
262 bounds.maxZ = std::max(bounds.maxZ, mesh.positions()[i + 2]);
263 }
264 bounds.minY -= tolerance;
265 bounds.maxY += tolerance;
266 bounds.valid = true;
267 return result;
268}
269
271 SpatialData result;
272 result.kind_ = Kind::Union;
273 result.left_ = std::make_shared<SpatialData>(a);
274 result.right_ = std::make_shared<SpatialData>(b);
275 result.bounds_ = unionBounds(a.bounds(), b.bounds());
276 return result;
277}
278
280 SpatialData result;
281 result.kind_ = Kind::Intersection;
282 result.left_ = std::make_shared<SpatialData>(a);
283 result.right_ = std::make_shared<SpatialData>(b);
284 result.bounds_ = intersectionBounds(a.bounds(), b.bounds());
285 return result;
286}
287
289 SpatialData result;
290 result.kind_ = Kind::Difference;
291 result.left_ = std::make_shared<SpatialData>(a);
292 result.right_ = std::make_shared<SpatialData>(b);
293 result.bounds_ = a.bounds();
294 return result;
295}
296
297std::string SpatialData::getKind() const {
298 switch (kind_) {
299 case Kind::Points: return "points";
300 case Kind::Box: return "volume.box";
301 case Kind::Sphere: return "volume.sphere";
302 case Kind::Polygon: return "volume.polygon";
303 case Kind::Spline: return "spline";
304 case Kind::Heightfield: return "surface.heightfield";
305 case Kind::TextureMask: return "volume.texture_mask";
306 case Kind::MeshSurface: return "surface.mesh";
307 case Kind::Union: return "union";
308 case Kind::Intersection: return "intersection";
309 case Kind::Difference: return "difference";
310 default: return "empty";
311 }
312}
313
314bool SpatialData::contains(float x, float y, float z) const {
315 switch (kind_) {
316 case Kind::Points:
317 for (const auto& point : points_.points())
318 if (point.x == x && point.y == y && point.z == z) return true;
319 return false;
320 case Kind::Box:
321 return x >= bounds_.minX && x <= bounds_.maxX && y >= bounds_.minY && y <= bounds_.maxY &&
322 z >= bounds_.minZ && z <= bounds_.maxZ;
323 case Kind::Sphere: {
324 const float dx = x - centerX_;
325 const float dy = y - centerY_;
326 const float dz = z - centerZ_;
327 return dx * dx + dy * dy + dz * dz <= radius_ * radius_;
328 }
329 case Kind::Polygon: return y >= bounds_.minY && y <= bounds_.maxY && pointInPolygon(x, z, points_.points());
330 case Kind::Spline:
331 for (size_t i = 1; i < points_.points().size(); ++i)
332 if (pointSegmentDistanceSquared(x, y, z, points_.points()[i - 1], points_.points()[i]) <=
333 radius_ * radius_)
334 return true;
335 return false;
336 case Kind::Heightfield: {
337 if (x < bounds_.minX || x > bounds_.maxX || z < bounds_.minZ || z > bounds_.maxZ) return false;
338 const float hx = (x - originX_) / cellSize_;
339 const float hz = (z - originZ_) / cellSize_;
340 const float surface = heightmap_->sampleBilinear(hx, hz) * heightScale_;
341 return std::abs(y - surface) <= cellSize_ * 0.5f;
342 }
343 case Kind::TextureMask: {
344 if (x < bounds_.minX || x > bounds_.maxX || y < bounds_.minY || y > bounds_.maxY || z < bounds_.minZ ||
345 z > bounds_.maxZ)
346 return false;
347 const float value = heightmap_->sampleBilinear((x - originX_) / cellSize_, (z - originZ_) / cellSize_);
348 return value >= minValue_ && value <= maxValue_;
349 }
350 case Kind::MeshSurface: {
351 float surfaceY = 0.f, nx = 0.f, ny = 1.f, nz = 0.f;
352 return closestMeshHeight(*mesh_, x, z, y, surfaceY, nx, ny, nz) && std::abs(y - surfaceY) <= radius_;
353 }
354 case Kind::Union: return left_->contains(x, y, z) || right_->contains(x, y, z);
355 case Kind::Intersection: return left_->contains(x, y, z) && right_->contains(x, y, z);
356 case Kind::Difference: return left_->contains(x, y, z) && !right_->contains(x, y, z);
357 default: return false;
358 }
359}
360
361SpatialBounds SpatialData::bounds() const { return bounds_; }
362bool SpatialData::hasBounds() const { return bounds_.valid; }
363float SpatialData::getMinX() const { return bounds_.valid ? bounds_.minX : 0.f; }
364float SpatialData::getMinY() const { return bounds_.valid ? bounds_.minY : 0.f; }
365float SpatialData::getMinZ() const { return bounds_.valid ? bounds_.minZ : 0.f; }
366float SpatialData::getMaxX() const { return bounds_.valid ? bounds_.maxX : 0.f; }
367float SpatialData::getMaxY() const { return bounds_.valid ? bounds_.maxY : 0.f; }
368float SpatialData::getMaxZ() const { return bounds_.valid ? bounds_.maxZ : 0.f; }
369
370float SpatialData::sampleScalar(float x, float y, float z, float outsideValue) const {
371 switch (kind_) {
372 case Kind::Heightfield: {
373 if (!heightmap_ || cellSize_ <= 0.f || x < bounds_.minX || x > bounds_.maxX || z < bounds_.minZ ||
374 z > bounds_.maxZ)
375 return outsideValue;
376 return heightmap_->sampleBilinear((x - originX_) / cellSize_, (z - originZ_) / cellSize_) * heightScale_;
377 }
378 case Kind::TextureMask: {
379 if (!heightmap_ || cellSize_ <= 0.f || x < bounds_.minX || x > bounds_.maxX || y < bounds_.minY ||
380 y > bounds_.maxY || z < bounds_.minZ || z > bounds_.maxZ)
381 return outsideValue;
382 return heightmap_->sampleBilinear((x - originX_) / cellSize_, (z - originZ_) / cellSize_);
383 }
384 case Kind::MeshSurface: {
385 if (!mesh_) return outsideValue;
386 float surfaceY = 0.f, nx = 0.f, ny = 1.f, nz = 0.f;
387 if (!closestMeshHeight(*mesh_, x, z, y, surfaceY, nx, ny, nz)) return outsideValue;
388 return surfaceY;
389 }
390 default:
391 return contains(x, y, z) ? 1.f : outsideValue;
392 }
393}
394
395PointSet sampleSpatialOntoChannel(const PointSet& input, const SpatialData& spatial, std::string_view channel,
396 float inputMin, float inputMax, float outputMin, float outputMax, bool clampOutput,
397 bool invert) {
398 std::string target(channel);
399 if (target.empty()) target = "$Density";
400 float rangeMin = inputMin;
401 float rangeMax = inputMax;
402 if (rangeMin == rangeMax) {
403 if (spatial.getKind() == "surface.heightfield") {
404 rangeMin = spatial.getMinY();
405 rangeMax = spatial.getMaxY();
406 } else {
407 rangeMin = 0.f;
408 rangeMax = 1.f;
409 }
410 if (rangeMin == rangeMax) rangeMax = rangeMin + 1.f;
411 }
412 const float range = rangeMax - rangeMin;
413 PointSet result = input;
414 for (int index = 0; index < result.getCount(); ++index) {
415 const float raw = spatial.sampleScalar(result.getX(index), result.getY(index), result.getZ(index), rangeMin);
416 float t = (raw - rangeMin) / range;
417 if (clampOutput) t = std::clamp(t, 0.f, 1.f);
418 if (invert) t = 1.f - t;
419 const float value = outputMin + t * (outputMax - outputMin);
420 writePointFloatChannel(result, index, target, value).expect("sampleSpatialOntoChannel");
421 }
422 return result;
423}
424
425PointSet SpatialData::sample(float spacing, uint32_t seed, float jitter) const {
427 if (!bounds_.valid || spacing <= 0.f) return output;
428 jitter = std::clamp(jitter, 0.f, 1.f);
429 const float extent = spacing * jitter * 0.5f;
430 uint32_t index = 0;
431 if (kind_ == Kind::Heightfield || kind_ == Kind::MeshSurface) {
432 for (float z = bounds_.minZ; z <= bounds_.maxZ + spacing * 0.001f; z += spacing) {
433 for (float x = bounds_.minX; x <= bounds_.maxX + spacing * 0.001f; x += spacing) {
434 const uint32_t pointSeed = mixSpatialSeed(seed ^ index++);
435 const float sx =
436 std::clamp(x + (unitFloat(pointSeed) * 2.f - 1.f) * extent, bounds_.minX, bounds_.maxX);
437 const float sz = std::clamp(z + (unitFloat(pointSeed ^ 0x02e5be93u) * 2.f - 1.f) * extent, bounds_.minZ,
438 bounds_.maxZ);
439 const int added = output.add(sx, 0.f, sz);
440 output.setPointSeed(added, pointSeed);
441 }
442 }
443 return project(output);
444 }
445 for (float z = bounds_.minZ; z <= bounds_.maxZ + spacing * 0.001f; z += spacing) {
446 for (float y = bounds_.minY; y <= bounds_.maxY + spacing * 0.001f; y += spacing) {
447 for (float x = bounds_.minX; x <= bounds_.maxX + spacing * 0.001f; x += spacing) {
448 const uint32_t pointSeed = mixSpatialSeed(seed ^ index++);
449 const float sx = bounds_.maxX > bounds_.minX ? x + (unitFloat(pointSeed) * 2.f - 1.f) * extent : x;
450 const float sy =
451 bounds_.maxY > bounds_.minY ? y + (unitFloat(pointSeed ^ 0x68bc21ebu) * 2.f - 1.f) * extent : y;
452 const float sz =
453 bounds_.maxZ > bounds_.minZ ? z + (unitFloat(pointSeed ^ 0x02e5be93u) * 2.f - 1.f) * extent : z;
454 if (!contains(sx, sy, sz)) continue;
455 const int added = output.add(sx, sy, sz);
456 output.setPointSeed(added, pointSeed);
457 }
458 }
459 }
460 return output;
461}
462
463PointSet SpatialData::filter(const PointSet& input, bool invert) const {
465 for (size_t index = 0; index < input.points().size(); ++index) {
466 const auto& point = input.points()[index];
467 if (contains(point.x, point.y, point.z) != invert)
468 std::move(output.appendPointFrom(input, index)).expect("SpatialData filter attribute schema");
469 }
470 return output;
471}
472
474 if (kind_ != Kind::Heightfield && kind_ != Kind::MeshSurface) return input;
476 for (size_t index = 0; index < output.points().size(); ++index) {
478 if (point.x < bounds_.minX || point.x > bounds_.maxX || point.z < bounds_.minZ || point.z > bounds_.maxZ)
479 continue;
480 if (kind_ == Kind::MeshSurface) {
481 float y = 0.f, nx = 0.f, ny = 1.f, nz = 0.f;
482 if (!closestMeshHeight(*mesh_, point.x, point.z, point.y, y, nx, ny, nz)) continue;
483 point.y = y;
484 point.normalX = nx;
485 point.normalY = ny;
486 point.normalZ = nz;
487 continue;
488 }
489 const float hx = (point.x - originX_) / cellSize_;
490 const float hz = (point.z - originZ_) / cellSize_;
491 point.y = heightmap_->sampleBilinear(hx, hz) * heightScale_;
492 const float left = heightmap_->sampleBilinear(hx - 0.5f, hz) * heightScale_;
493 const float right = heightmap_->sampleBilinear(hx + 0.5f, hz) * heightScale_;
494 const float down = heightmap_->sampleBilinear(hx, hz - 0.5f) * heightScale_;
495 const float up = heightmap_->sampleBilinear(hx, hz + 0.5f) * heightScale_;
496 float nx = -(right - left) / cellSize_;
497 float ny = 1.f;
498 float nz = -(up - down) / cellSize_;
499 const float length = std::sqrt(nx * nx + ny * ny + nz * nz);
500 if (length > 0.f) {
501 nx /= length;
502 ny /= length;
503 nz /= length;
504 }
505 point.normalX = nx;
506 point.normalY = ny;
507 point.normalZ = nz;
508 }
509 return output;
510}
511
512} // namespace eve::procgen
LogicalId target
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string output
eve::action::ActionSpatialBinding spatial
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
int bz
Definition CaveMesh.cpp:114
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
float length
Definition CaveMesh.cpp:94
int az
Definition CaveMesh.cpp:113
int by
Definition CaveMesh.cpp:114
float py
float nx
float nz
float ny
float pz
std::map< std::string, Var > values
EvpackChunkInput input
Definition Evpack.cpp:170
std::vector< std::uint32_t > indices
std::vector< float > positions
HexVec3 up
HexVec3 left
HexVec3 right
std::int32_t c
std::vector< Colorf > px
Range range
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
graphics::Canvas * previous
float radius
std::uint32_t seed
Definition PointSet.cpp:807
std::shared_ptr< const std::vector< glm::vec2 > > points
float t
Mesh * mesh
bool found
float dz
float dy
float dx
int spacing
float size
Definition TreeMesh.cpp:156
uint32_t index
std::vector< char > inside
glm::vec3 point
float vz
float vy
float vx
void expect(std::string_view message) const
Require success for a void operation.
Definition Result.h:548
In-memory terrain heightmap: a dense float grid (row-major, index = y * width + x) materialized from ...
Definition Heightmap.h:21
int getHeight() const
Returns the height.
Definition Heightmap.cpp:17
float height(int x, int y) const
Height.
Definition Heightmap.cpp:28
int getWidth() const
Returns the width.
Definition Heightmap.cpp:16
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:19
Script-friendly collection of attributed 3D samples.
Definition PointSet.h:59
float getY(int index) const
Returns the y.
Definition PointSet.cpp:125
ProcgenPoint & mutablePoint(std::size_t index)
Mutably access one existing point without changing row structure.
Definition PointSet.cpp:92
float getX(int index) const
Returns the x.
Definition PointSet.cpp:121
float getZ(int index) const
Returns the z.
Definition PointSet.cpp:129
const std::vector< ProcgenPoint > & points() const
Borrow immutable point rows; structural ownership remains with this set.
Definition PointSet.h:212
int getCount() const
Returns the count.
Definition PointSet.cpp:48
Queryable spatial input for procedural pipelines.
Definition SpatialData.h:35
static SpatialData meshSurface(const MeshBuild &mesh, float tolerance)
Create a queryable triangle surface from CPU mesh data.
PointSet filter(const PointSet &input, bool invert=false) const
Filter attributed points by this domain.
static SpatialData spline(const PointSet &controlPoints, float radius)
Create a polyline domain with a radial influence width.
static SpatialData unite(const SpatialData &a, const SpatialData &b)
Create the union of two domains.
SpatialBounds bounds() const
Conservative world-space bounds, or invalid bounds for an empty domain.
static SpatialData fromPoints(const PointSet &points)
Create spatial data whose domain is the bounds of attributed points.
static SpatialData box(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Create an axis-aligned volume; unordered endpoints are normalized.
static SpatialData textureMask(const Heightmap &values, float originX, float originZ, float cellSize, float minValue, float maxValue, float minY, float maxY)
Create an extruded spatial mask from scalar texture data.
static SpatialData subtract(const SpatialData &a, const SpatialData &b)
Create the part of a outside b.
static SpatialData sphere(float x, float y, float z, float radius)
Create a spherical volume. A non-positive radius produces an empty domain.
bool contains(float x, float y, float z) const
Whether this domain contains a world-space position.
std::string getKind() const
Stable textual kind used by scripts and diagnostics.
static SpatialData polygon(const PointSet &controlPoints, float minY, float maxY)
Create an XZ polygon prism between two world-space heights.
PointSet sample(float spacing, uint32_t seed, float jitter) const
Deterministically sample a 3D lattice inside the domain.
static SpatialData intersect(const SpatialData &a, const SpatialData &b)
Create the intersection of two domains.
float sampleScalar(float x, float y, float z, float outsideValue=0.f) const
Sample a continuous scalar at a world position.
static SpatialData heightfield(const Heightmap &heightmap, float originX, float originZ, float cellSize, float heightScale)
Create a sampled heightfield surface in world space.
PointSet project(const PointSet &input) const
Project points onto a heightfield surface; other kinds return an unchanged copy.
bool pointInPolygon(const Vec2 &p, const Polygon &poly)
Point-in-polygon test (ray casting; boundary counts as inside).
PointSet sampleSpatialOntoChannel(const PointSet &input, const SpatialData &spatial, std::string_view channel, float inputMin, float inputMax, float outputMin, float outputMax, bool clampOutput, bool invert)
Remap SpatialData::sampleScalar onto a float channel ($Density or metadata). When inputMin equals inp...
Result< void > writePointFloatChannel(PointSet &points, int index, std::string_view name, float value)
Write a float metadata column or closed $ point-field selector.
Axis-aligned bounds shared by all procedural spatial data types.
Definition SpatialData.h:18