载入中...
搜索中...
未找到
TerrainObjectPlacement.cpp
浏览该文件的文档.
23 return static_cast<float>(x + y) / static_cast<float>(std::numeric_limits<std::uint64_t>::max());
39bool ordered(float minimum, float maximum) { return std::isfinite(minimum) && std::isfinite(maximum) && maximum >= minimum; }
43 return mode >= TerrainObjectScaleMode::Fixed && mode <= TerrainObjectScaleMode::FitnessRandomized;
46 return mode >= TerrainObjectYOffsetMode::TerrainHeight && mode <= TerrainObjectYOffsetMode::Custom;
49Normal normalAt(const Heightmap& heights, double u, double v, double width, double depth, double heightScale) {
70 return !i.asset.empty() && i.minimumInstances >= 0 && i.maximumInstances >= i.minimumInstances &&
73 validYOffset(i.yOffsetMode) && std::isfinite(i.customOffset) && validScale(i.scaleMode) && scales &&
76 ordered(i.minimumRotationX, i.maximumRotationX) && ordered(i.minimumRotationY, i.maximumRotationY) &&
86Result<int> TerrainObjectPlacementSettings::addInstance(const TerrainObjectInstanceSettings& instance) {
87 if (!validInstance(instance)) return invalid("terrain.objectSettings: valid resource instance required");
92Result<int> exportTerrainObjectPoints(PointSet& output, const Heightmap& fitness, const Heightmap& heights,
95 if (!validRaster(fitness) || !validRaster(heights) || !std::isfinite(s.originX) || !std::isfinite(s.originZ) ||
96 !positive(s.width) || !positive(s.depth) || !std::isfinite(s.heightScale) || !positive(s.spacing) ||
100 !std::isfinite(s.boundsCheckQuality) || s.boundsCheckQuality < 0 || s.boundsCheckQuality > 100 ||
101 !positive(s.prototypeScale) || !std::isfinite(s.startOffsetX) || !std::isfinite(s.startOffsetZ) ||
104 return invalid("terrain.objectPoints: valid rasters, prototype, instances, probabilities and transforms required");
108 if (!isRepresentable(increment) || increment <= 0) return invalid("terrain.objectPoints: spacing is not representable");
116 for (double z = s.startOffsetZ; z <= double(s.depth) + increment; z += increment, ++candidate) {
125 if (s.boundsCollisionCheck && std::any_of(acceptedCenters.begin(), acceptedCenters.end(), [&](const auto& p) {
128 const int rx = std::max(0, int(std::round(s.boundsRadius * (fitness.getWidth() - 1) / s.width)));
129 const int rz = std::max(0, int(std::round(s.boundsRadius * (fitness.getHeight() - 1) / s.depth)));
130 const int cx = int(std::round(u * (fitness.getWidth() - 1))), cz = int(std::round(v * (fitness.getHeight() - 1)));
136 if (px >= 0 && pz >= 0 && px < fitness.getWidth() && pz < fitness.getHeight()) sum += fitness.height(px, pz);
147 const double offsetX = random.next(instance.minimumOffsetX, instance.maximumOffsetX) * s.prototypeScale;
148 const double offsetZ = random.next(instance.minimumOffsetZ, instance.maximumOffsetZ) * s.prototypeScale;
164 if (instance.commonScale) sx = sy = sz = random.next(instance.minimumScale, instance.maximumScale);
165 else { sx = random.next(instance.minimumScaleX, instance.maximumScaleX); sy = random.next(instance.minimumScaleY, instance.maximumScaleY); sz = random.next(instance.minimumScaleZ, instance.maximumScaleZ); }
166 } else if (instance.scaleMode == TerrainObjectScaleMode::Fitness || instance.scaleMode == TerrainObjectScaleMode::FitnessRandomized) {
167 if (instance.commonScale) sx = sy = sz = std::lerp(instance.minimumScale, instance.maximumScale, instanceStrength);
168 else { sx = std::lerp(instance.minimumScaleX, instance.maximumScaleX, instanceStrength); sy = std::lerp(instance.minimumScaleY, instance.maximumScaleY, instanceStrength); sz = std::lerp(instance.minimumScaleZ, instance.maximumScaleZ, instanceStrength); }
171 if (instance.commonScale) { const double factor = random.next(1 - instance.scaleRandomPercentage, 1 + instance.scaleRandomPercentage); sx *= factor; sy *= factor; sz *= factor; }
172 else { sx *= random.next(1 - instance.scaleRandomPercentageX, 1 + instance.scaleRandomPercentageX); sy *= random.next(1 - instance.scaleRandomPercentageY, 1 + instance.scaleRandomPercentageY); sz *= random.next(1 - instance.scaleRandomPercentageZ, 1 + instance.scaleRandomPercentageZ); }
177 if (instance.yOffsetAlongSlope) { finalX += normal.x * yOffset; finalY += normal.y * yOffset; finalZ += normal.z * yOffset; }
180 double yaw = random.next(instance.minimumRotationY + direction, instance.maximumRotationY + direction);
182 if (instance.alignForwardToSlope) yaw += std::atan2(normal.x, normal.z) * 180.0 / 3.14159265358979323846;
183 if (instance.rotateToSlope) { pitch += std::atan2(-normal.z, normal.y) * 180.0 / 3.14159265358979323846; roll += std::atan2(normal.x, normal.y) * 180.0 / 3.14159265358979323846; }
195 point.normalX = float(normal.x); point.normalY = float(normal.y); point.normalZ = float(normal.z);
197 point.scaleX = float(sx); point.scaleY = float(sy); point.scaleZ = float(sz); point.density = float(instanceStrength);
198 point.boundsMinX = point.boundsMinZ = -float(radius); point.boundsMaxX = point.boundsMaxZ = float(radius); point.boundsMaxY = float(sy);
201 auto asset = next.trySetStringAttribute(row, "asset", instance.asset); if (!asset.ok()) return Result<int>::failure(asset.status());
202 auto prototype = next.trySetStringAttribute(row, "objectPrototype", s.prototype); if (!prototype.ok()) return Result<int>::failure(prototype.status());
203 auto source = next.trySetStringAttribute(row, "spawnNamespace", std::to_string(s.namespaceId)); if (!source.ok()) return Result<int>::failure(source.status());
204 auto candidateAttr = next.trySetIntAttribute(row, "objectCandidate", static_cast<std::int64_t>(candidate)); if (!candidateAttr.ok()) return Result<int>::failure(candidateAttr.status());
205 auto resourceAttr = next.trySetIntAttribute(row, "objectResource", static_cast<std::int64_t>(resource)); if (!resourceAttr.ok()) return Result<int>::failure(resourceAttr.status());
216Result<int> removeTerrainObjectPoints(PointSet& output, const PointSet& input, const Heightmap& fitness,
219 if (!validRaster(fitness) || !std::all_of(fitness.data().begin(), fitness.data().end(), normalized) ||
220 !std::isfinite(s.originX) || !std::isfinite(s.originZ) || !positive(s.width) || !positive(s.depth) ||
222 return invalid("terrain.removeObjectPoints: valid fitness, domain, prototype and threshold required");
230 else { auto copied = next.appendPointFrom(input, static_cast<std::size_t>(i)); if (!copied.ok()) return Result<int>::failure(copied.status()); }
238 TerrainObjectOperationMode mode, bool worldMapOperation, const std::vector<std::string>& validTerrainNames) {
240 const bool validMode = mode == TerrainObjectOperationMode::Add || mode == TerrainObjectOperationMode::Replace ||
246 !std::isfinite(s.boundsCheckQuality) || s.boundsCheckQuality < 0 || s.boundsCheckQuality > 100 ||
247 !positive(s.prototypeScale) || !std::isfinite(s.startOffsetX) || !std::isfinite(s.startOffsetZ) ||
248 !std::isfinite(s.heightScale) || !std::isfinite(s.seaLevel) || s.namespaceId == 0 || s.maxPoints < 0 ||
249 s.prototype.empty() || s.instances.empty() || !std::all_of(s.instances.begin(), s.instances.end(), validInstance))
251 DiagnosticCode::InvalidArgument, "terrain.object.multitile: valid tiles, operation and prototype required"));
254 DiagnosticCode::InvalidArgument, "terrain.object.multitile: normalized operation fitness required"));
258 if (!tile.objects || !tile.heights || !owners.insert(tile.objects).second || !validRaster(*tile.heights))
260 DiagnosticCode::InvalidArgument, "terrain.object.multitile: distinct outputs and finite heights required"));
264 auto mapped = mapTerrainOperationMultiTile(descriptors, operationSettings, TerrainOperationDomain::GameObject,
268 if (fitness.getWidth() != report.operationWidth || fitness.getHeight() != report.operationHeight)
270 DiagnosticCode::InvalidArgument, "terrain.object.multitile: fitness dimensions must match operation window"));
274 DiagnosticCode::InvalidArgument, "terrain.object.multitile: effective spacing is not representable"));
288 DiagnosticCode::InvariantViolation, "terrain.object.multitile: mapped terrain was not supplied"));
291 const bool clearFirst = mode == TerrainObjectOperationMode::Replace || mode == TerrainObjectOperationMode::Remove;
296 const int lx = nearestEven((double(point.x) - tile->originX) * tile->objectResolutionX / tile->width);
297 const int ly = nearestEven((double(point.z) - tile->originZ) * tile->objectResolutionY / tile->depth);
315 const double stopX = (mapping.localX + mapping.width) * tile->width / tile->objectResolutionX + increment;
316 const double stopZ = (mapping.localY + mapping.height) * tile->depth / tile->objectResolutionY + increment;
318 for (double x = startX; x <= stopX; x += increment) for (double z = startZ; z <= stopZ; z += increment, ++globalCandidate) {
330 if (s.boundsCollisionCheck && std::any_of(acceptedCenters.begin(), acceptedCenters.end(), [&](const auto& p) {
334 const int rx = std::max(0, int(std::round(s.boundsRadius * tile->objectResolutionX / tile->width)));
335 const int ry = std::max(0, int(std::round(s.boundsRadius * tile->objectResolutionY / tile->depth)));
338 for (int px = lx - rx; px <= lx + rx; px += stepX) for (int py = ly - ry; py <= ly + ry; py += stepY) {
339 if (px >= mapping.localX && px < mapping.localX + mapping.width && py >= mapping.localY && py < mapping.localY + mapping.height)
340 sum += fitness.height(mapping.operationX + px - mapping.localX, mapping.operationY + py - mapping.localY);
352 const double ox = random.next(instance.minimumOffsetX, instance.maximumOffsetX) * s.prototypeScale;
353 const double oz = random.next(instance.minimumOffsetZ, instance.maximumOffsetZ) * s.prototypeScale;
375 if (instance.commonScale) sx = sy = sz = random.next(instance.minimumScale, instance.maximumScale);
376 else { sx = random.next(instance.minimumScaleX, instance.maximumScaleX); sy = random.next(instance.minimumScaleY, instance.maximumScaleY); sz = random.next(instance.minimumScaleZ, instance.maximumScaleZ); }
377 } else if (instance.scaleMode == TerrainObjectScaleMode::Fitness || instance.scaleMode == TerrainObjectScaleMode::FitnessRandomized) {
378 if (instance.commonScale) sx = sy = sz = std::lerp(instance.minimumScale, instance.maximumScale, instanceStrength);
379 else { sx = std::lerp(instance.minimumScaleX, instance.maximumScaleX, instanceStrength); sy = std::lerp(instance.minimumScaleY, instance.maximumScaleY, instanceStrength); sz = std::lerp(instance.minimumScaleZ, instance.maximumScaleZ, instanceStrength); }
382 if (instance.commonScale) { const double factor = random.next(1 - instance.scaleRandomPercentage, 1 + instance.scaleRandomPercentage); sx *= factor; sy *= factor; sz *= factor; }
383 else { sx *= random.next(1 - instance.scaleRandomPercentageX, 1 + instance.scaleRandomPercentageX); sy *= random.next(1 - instance.scaleRandomPercentageY, 1 + instance.scaleRandomPercentageY); sz *= random.next(1 - instance.scaleRandomPercentageZ, 1 + instance.scaleRandomPercentageZ); }
388 if (instance.yOffsetAlongSlope) { fx += normal.x * yOffset; fy += normal.y * yOffset; fz += normal.z * yOffset; } else fy += yOffset;
390 double yaw = random.next(instance.minimumRotationY + direction, instance.maximumRotationY + direction);
392 if (instance.alignForwardToSlope) yaw += std::atan2(normal.x, normal.z) * 180.0 / 3.14159265358979323846;
393 if (instance.rotateToSlope) { pitch += std::atan2(-normal.z, normal.y) * 180.0 / 3.14159265358979323846; roll += std::atan2(normal.x, normal.y) * 180.0 / 3.14159265358979323846; }
399 DiagnosticCode::InvalidArgument, "terrain.object.multitile: generated transform is not representable"));
401 point.id = mix(mix(mix(mix(mix(s.namespaceId) ^ stableNameHash(tile->name)) ^ (globalCandidate + 1)) ^
406 point.normalX = float(normal.x); point.normalY = float(normal.y); point.normalZ = float(normal.z);
408 point.scaleX = float(sx); point.scaleY = float(sy); point.scaleZ = float(sz); point.density = instanceStrength;
409 point.boundsMinX = point.boundsMinZ = -float(radius); point.boundsMaxX = point.boundsMaxZ = float(radius); point.boundsMaxY = float(sy);
412 auto a = next.trySetStringAttribute(row, "asset", instance.asset); if (!a.ok()) return Result<TerrainMultiTileReport>::failure(a.status());
413 auto p = next.trySetStringAttribute(row, "objectPrototype", s.prototype); if (!p.ok()) return Result<TerrainMultiTileReport>::failure(p.status());
414 auto source = next.trySetStringAttribute(row, "spawnNamespace", std::to_string(s.namespaceId)); if (!source.ok()) return Result<TerrainMultiTileReport>::failure(source.status());
415 auto c = next.trySetIntAttribute(row, "objectCandidate", static_cast<std::int64_t>(mix(stableNameHash(tile->name)) ^ mix(globalCandidate + 1))); if (!c.ok()) return Result<TerrainMultiTileReport>::failure(c.status());
416 auto r = next.trySetIntAttribute(row, "objectResource", static_cast<std::int64_t>(resource)); if (!r.ok()) return Result<TerrainMultiTileReport>::failure(r.status());
424 DiagnosticCode::InvalidArgument, "terrain.object.multitile: changed point count exceeds range"));
433 struct Tile { std::string name; PointSet objects; Heightmap heights; double originX, originZ, width, depth; int rx, ry; bool worldMap; };
435 std::vector<Tile> tiles; TerrainMultiTileReport last; std::vector<Snapshot> history; int cursor = 0;
436 Snapshot snapshot() const { Snapshot result; for (const auto& tile : tiles) result.objects.push_back(tile.objects); result.report = last; return result; }
441TerrainMultiObjectWorkspace::TerrainMultiObjectWorkspace(TerrainMultiObjectWorkspace&&) noexcept = default;
442TerrainMultiObjectWorkspace& TerrainMultiObjectWorkspace::operator=(TerrainMultiObjectWorkspace&&) noexcept = default;
443Result<int> TerrainMultiObjectWorkspace::addTile(const std::string& name, const PointSet& objects, const Heightmap& heights,
446 if (!impl_ || name.empty() || !raster_detail::validRaster(heights) || !std::isfinite(originX) ||
447 !std::isfinite(originZ) || !std::isfinite(width) || width <= 0 || !std::isfinite(depth) || depth <= 0 ||
450 if (impl_->history.size() > 1 || std::any_of(impl_->tiles.begin(), impl_->tiles.end(), [&](const auto& t) { return t.name == name; }))
457Result<int> TerrainMultiObjectWorkspace::apply(const Heightmap& fitness, const TerrainObjectPlacementSettings& settings,
460 if (!impl_ || impl_->tiles.empty()) return invalid("terrain.object.workspace: populated workspace required");
462 for (auto& tile : candidate->tiles) tiles.push_back({tile.name, &tile.objects, &tile.heights, tile.originX, tile.originZ, tile.width, tile.depth, tile.rx, tile.ry, tile.worldMap});
463 auto result = applyTerrainObjectsMultiTile(tiles, fitness, settings, operationSettings, mode, worldMapOperation);
465 candidate->last = std::move(result.value()); candidate->history.resize(std::size_t(candidate->cursor + 1));
469Result<int> TerrainMultiObjectWorkspace::copyTile(const std::string& name, PointSet& output) const {
471 const auto found = std::find_if(impl_->tiles.begin(), impl_->tiles.end(), [&](const auto& tile) { return tile.name == name; });
475Result<int> TerrainMultiObjectWorkspace::undo() { if (!impl_ || impl_->cursor <= 0) return invalid("terrain.object.workspace: no undo snapshot"); --impl_->cursor; impl_->restore(impl_->history[std::size_t(impl_->cursor)]); return Result<int>::success(impl_->cursor); }
476Result<int> TerrainMultiObjectWorkspace::redo() { if (!impl_ || impl_->cursor + 1 >= int(impl_->history.size())) return invalid("terrain.object.workspace: no redo snapshot"); ++impl_->cursor; impl_->restore(impl_->history[std::size_t(impl_->cursor)]); return Result<int>::success(impl_->cursor); }
477int TerrainMultiObjectWorkspace::getTileCount() const noexcept { return impl_ ? int(impl_->tiles.size()) : 0; }
478int TerrainMultiObjectWorkspace::getLastChangedSamples() const noexcept { return impl_ ? impl_->last.changedSamples : 0; }
479int TerrainMultiObjectWorkspace::getLastAffectedTiles() const noexcept { return impl_ ? impl_->last.affectedTiles : 0; }
480int TerrainMultiObjectWorkspace::getOperationCount() const noexcept { return impl_ ? std::max(0, int(impl_->history.size()) - 1) : 0; }
481int TerrainMultiObjectWorkspace::getAppliedCount() const noexcept { return impl_ ? impl_->cursor : 0; }
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
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
In-memory terrain heightmap: a dense float grid (row-major, index = y * width + x) materialized from ...
Definition Heightmap.h:21
Owning transactional multi-terrain object workspace with undo and redo snapshots.
Definition TerrainObjectPlacement.h:115
TerrainMultiObjectWorkspace()
Terrain multi object workspace.
Definition TerrainObjectPlacement.cpp:439
~TerrainMultiObjectWorkspace()
Terrain multi object workspace.
Result< int > apply(const Heightmap &fitness, const TerrainObjectPlacementSettings &settings, const TerrainStampSettings &operationSettings, TerrainObjectOperationMode mode, bool worldMapOperation=false)
Apply one operation as a single atomic history entry.
Definition TerrainObjectPlacement.cpp:457
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
double textureSample(const Heightmap &map, double u, double v)
Texture sample.
Definition TerrainRasterInternal.h:38
Definition EvpackPointGraphLoader.h:12
Result< int > removeTerrainObjectPoints(PointSet &output, const PointSet &input, const Heightmap &fitness, const TerrainObjectPlacementSettings &s, float removalStrength)
Remove matching prototype points wherever fitness is strictly above removalStrength.
Definition TerrainObjectPlacement.cpp:216
Result< TerrainMultiTileReport > applyTerrainObjectsMultiTile(const std::vector< TerrainObjectTile > &tiles, const Heightmap &fitness, const TerrainObjectPlacementSettings &s, const TerrainStampSettings &operationSettings, TerrainObjectOperationMode mode, bool worldMapOperation, const std::vector< std::string > &validTerrainNames)
Apply one Pcg-style game-object rule over mapped terrain tiles atomically.
Definition TerrainObjectPlacement.cpp:235
Result< TerrainMultiTileReport > mapTerrainOperationMultiTile(const std::vector< TerrainOperationTile > &tiles, const TerrainStampSettings &settings, TerrainOperationDomain domain, bool worldMapOperation, const std::vector< std::string > &validTerrainNames)
Calculate Pcg-compatible local and shared pixel rectangles for any multi-terrain raster domain.
Definition TerrainMultiTile.cpp:71
TerrainObjectScaleMode
Pcg-compatible scale policy for one terrain object instance resource.
Definition TerrainObjectPlacement.h:17
@ FitnessRandomized
@ GameObject
Result< int > exportTerrainObjectPoints(PointSet &output, const Heightmap &fitness, const Heightmap &heights, const TerrainObjectPlacementSettings &s)
Export Pcg-style terrain game-object resources into an attributed PointSet atomically.
Definition TerrainObjectPlacement.cpp:92
TerrainObjectYOffsetMode
Height reference used by one terrain object instance resource.
Definition TerrainObjectPlacement.h:19
@ TerrainHeight
TerrainObjectOperationMode
Mutation performed by a multi-terrain game-object operation.
Definition TerrainObjectPlacement.h:21
@ InvalidArgument
@ InvariantViolation
Definition Container.h:602
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
TerrainMultiTileReport report
Definition TerrainObjectPlacement.cpp:434
std::vector< PointSet > objects
Definition TerrainObjectPlacement.cpp:434
int ry
Definition TerrainObjectPlacement.cpp:433
int rx
Definition TerrainObjectPlacement.cpp:433
double originX
Definition TerrainObjectPlacement.cpp:433
double originZ
Definition TerrainObjectPlacement.cpp:433
double depth
Definition TerrainObjectPlacement.cpp:433
std::string name
Definition TerrainObjectPlacement.cpp:433
PointSet objects
Definition TerrainObjectPlacement.cpp:433
Heightmap heights
Definition TerrainObjectPlacement.cpp:433
bool worldMap
Definition TerrainObjectPlacement.cpp:433
double width
Definition TerrainObjectPlacement.cpp:433
Snapshot snapshot() const
Definition TerrainObjectPlacement.cpp:436
int cursor
Definition TerrainObjectPlacement.cpp:435
std::vector< Tile > tiles
Definition TerrainObjectPlacement.cpp:435
TerrainMultiTileReport last
Definition TerrainObjectPlacement.cpp:435
std::vector< Snapshot > history
Definition TerrainObjectPlacement.cpp:435
void restore(const Snapshot &value)
Definition TerrainObjectPlacement.cpp:437
Completed multi-tile stamp statistics and Pcg-compatible affected-pixel mappings.
Definition TerrainMultiTile.h:55
One resource entry emitted around each accepted Pcg game-object prototype location.
Definition TerrainObjectPlacement.h:24
Deterministic Pcg SetTerrainGameObjects scan, collision and resource configuration.
Definition TerrainObjectPlacement.h:50
std::vector< TerrainObjectInstanceSettings > instances
Definition TerrainObjectPlacement.h:63
Result< int > addInstance(const TerrainObjectInstanceSettings &instance)
Append one validated copied resource entry and return the new resource count.
Definition TerrainObjectPlacement.cpp:86
Value configuration for a rectangular stamp in world X/Z coordinates.
Definition TerrainStamp.h:22