载入中...
搜索中...
未找到
IncrementalBuild.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4#include "procgen/Semantic.h"
5
6#include <algorithm>
7#include <cmath>
8#include <limits>
9#include <set>
10#include <string_view>
11
12namespace eve::procgen {
13namespace {
14
15std::uint64_t keyFor(int x, int z) {
16 return (std::uint64_t(std::uint32_t(x)) << 32U) | std::uint32_t(z);
17}
18
19int keyX(std::uint64_t key) { return std::int32_t(key >> 32U); }
20int keyZ(std::uint64_t key) { return std::int32_t(key & 0xffffffffU); }
21
22void hashBytes(std::uint64_t& hash, const void* bytes, std::size_t count) {
23 const auto* data = static_cast<const unsigned char*>(bytes);
24 for (std::size_t index = 0; index < count; ++index) {
25 hash ^= data[index];
26 hash *= 1099511628211ULL;
27 }
28}
29
30template <class T>
31void hashValue(std::uint64_t& hash, const T& value) {
32 hashBytes(hash, &value, sizeof(value));
33}
34
35void hashString(std::uint64_t& hash, std::string_view value) {
36 hashBytes(hash, value.data(), value.size());
37 const unsigned char separator = 0xff;
38 hashBytes(hash, &separator, 1);
39}
40
41void hashPoint(std::uint64_t& hash, const PointSet& points, std::size_t row) {
42 const auto& point = points.points()[row];
43 hashValue(hash, point.id);
44 hashValue(hash, point.x);
45 hashValue(hash, point.y);
46 hashValue(hash, point.z);
47 hashValue(hash, point.normalX);
48 hashValue(hash, point.normalY);
49 hashValue(hash, point.normalZ);
50 hashValue(hash, point.pitch);
51 hashValue(hash, point.yaw);
52 hashValue(hash, point.roll);
53 hashValue(hash, point.scaleX);
54 hashValue(hash, point.scaleY);
55 hashValue(hash, point.scaleZ);
56 hashValue(hash, point.density);
57 hashValue(hash, point.seed);
58 hashValue(hash, point.boundsMinX);
59 hashValue(hash, point.boundsMinY);
60 hashValue(hash, point.boundsMinZ);
61 hashValue(hash, point.boundsMaxX);
62 hashValue(hash, point.boundsMaxY);
63 hashValue(hash, point.boundsMaxZ);
64 hashValue(hash, point.colorR);
65 hashValue(hash, point.colorG);
66 hashValue(hash, point.colorB);
67 hashValue(hash, point.colorA);
68 hashValue(hash, point.steepness);
69 const auto& attributes = points.attributes();
70 for (std::size_t column = 0; column < attributes.columnCount(); ++column) {
71 const auto name = attributes.columnName(column);
72 hashString(hash, name);
73 const auto type = attributes.typeOf(name);
74 if (!type || !attributes.has(row, name)) {
75 const std::uint8_t absent = 0;
76 hashValue(hash, absent);
77 continue;
78 }
79 const auto typeValue = std::uint8_t(*type) + 1;
80 hashValue(hash, typeValue);
81 switch (*type) {
82 case ProcgenAttributeType::Float: hashValue(hash, *attributes.getFloat(row, name)); break;
83 case ProcgenAttributeType::Int: hashValue(hash, *attributes.getInt(row, name)); break;
84 case ProcgenAttributeType::Bool: hashValue(hash, *attributes.getBool(row, name)); break;
86 const auto value = *attributes.getVector(row, name);
87 hashValue(hash, value.x);
88 hashValue(hash, value.y);
89 hashValue(hash, value.z);
90 break;
91 }
92 case ProcgenAttributeType::String: hashString(hash, *attributes.getString(row, name)); break;
93 }
94 }
95}
96
97int floorDivision(float value, float divisor) { return int(std::floor(value / divisor)); }
98
99bool gridContent(const Grid2D& grid, int clusterX, int clusterZ, int size) {
100 const int minX = std::max(0, clusterX * size);
101 const int minY = std::max(0, clusterZ * size);
102 const int maxX = std::min(grid.getWidth(), (clusterX + 1) * size);
103 const int maxY = std::min(grid.getHeight(), (clusterZ + 1) * size);
104 for (int y = minY; y < maxY; ++y)
105 for (int x = minX; x < maxX; ++x)
106 if (grid.getCell(x, y) != int(Semantic::Empty)) return true;
107 return false;
108}
109
110std::uint64_t clusterHash(const BuildLayerStack& stack, const Grid2D& grid, const PointSet& points,
111 const PointSet* orientation, int clusterX, int clusterZ, int size, float worldSize) {
112 std::uint64_t hash = 1469598103934665603ULL;
113 const auto definition = stack.serializeDefinition();
114 hashString(hash, definition);
115 const int minX = std::max(0, clusterX * size - 1);
116 const int minY = std::max(0, clusterZ * size - 1);
117 const int maxX = std::min(grid.getWidth(), (clusterX + 1) * size + 1);
118 const int maxY = std::min(grid.getHeight(), (clusterZ + 1) * size + 1);
119 for (int y = minY; y < maxY; ++y) {
120 for (int x = minX; x < maxX; ++x) {
121 const int cell = grid.getCell(x, y);
122 const int detail = grid.getDetail(x, y);
123 hashValue(hash, cell);
124 hashValue(hash, detail);
125 }
126 }
127 for (std::size_t index = 0; index < points.points().size(); ++index) {
128 const auto& point = points.points()[index];
129 if (floorDivision(point.x, worldSize) == clusterX && floorDivision(point.z, worldSize) == clusterZ)
130 hashPoint(hash, points, index);
131 }
132 if (orientation) {
133 for (std::size_t index = 0; index < orientation->points().size(); ++index) hashPoint(hash, *orientation, index);
134 }
135 return hash;
136}
137
138} // namespace
139
140int IncrementalBuildDelta::getCount() const noexcept { return int(changes_.size()); }
142 return index >= 0 && index < int(changes_.size()) ? changes_[std::size_t(index)].x : 0;
143}
145 return index >= 0 && index < int(changes_.size()) ? changes_[std::size_t(index)].z : 0;
146}
147bool IncrementalBuildDelta::isRemoved(int index) const noexcept {
148 return index >= 0 && index < int(changes_.size()) && changes_[std::size_t(index)].removed;
149}
151 if (index < 0 || index >= int(changes_.size()))
153 DiagnosticCode::InvalidArgument, "delta index is out of range", {}, {}, "procgen.incrementalBuild"));
154 if (changes_[std::size_t(index)].removed)
156 DiagnosticCode::NotFound, "removed cluster has no artifacts", {}, {}, "procgen.incrementalBuild"));
157 return Result<BuildLayerExecution>::success(changes_[std::size_t(index)].artifacts);
158}
159
161 const PointSet& points, int clusterSizeCells,
162 float cellSizeWorld, const PointSet* orientation) {
163 if (clusterSizeCells <= 0 || !std::isfinite(cellSizeWorld) || cellSizeWorld <= 0.f)
165 "cluster and cell sizes must be positive", {},
166 {}, "procgen.incrementalBuild"));
167 const float worldSize = float(clusterSizeCells) * cellSizeWorld;
168 std::set<std::pair<int, int>> candidates;
169 for (const auto& [key, record] : cache_) candidates.emplace(keyX(key), keyZ(key));
170 for (int y = 0; y < grid.getHeight(); ++y)
171 for (int x = 0; x < grid.getWidth(); ++x)
172 if (grid.getCell(x, y) != int(Semantic::Empty)) candidates.emplace(x / clusterSizeCells, y / clusterSizeCells);
173 for (const auto& point : points.points())
174 candidates.emplace(floorDivision(point.x, worldSize), floorDivision(point.z, worldSize));
175
177 std::vector<std::uint64_t> removals;
178 std::vector<std::pair<std::uint64_t, Record>> upserts;
179 for (const auto& [clusterX, clusterZ] : candidates) {
180 const auto key = keyFor(clusterX, clusterZ);
181 bool active = gridContent(grid, clusterX, clusterZ, clusterSizeCells);
182 if (!active) {
183 for (const auto& point : points.points()) {
184 if (floorDivision(point.x, worldSize) == clusterX && floorDivision(point.z, worldSize) == clusterZ) {
185 active = true;
186 break;
187 }
188 }
189 }
190 const auto previous = cache_.find(key);
191 if (!active) {
192 if (previous != cache_.end()) {
193 removals.push_back(key);
194 delta.changes_.push_back({clusterX, clusterZ, true, {}});
195 }
196 continue;
197 }
198 const auto hash = clusterHash(stack, grid, points, orientation, clusterX, clusterZ, clusterSizeCells, worldSize);
199 if (previous != cache_.end() && previous->second.hash == hash) continue;
200 const int minCellX = std::clamp(clusterX * clusterSizeCells, 0, grid.getWidth());
201 const int minCellY = std::clamp(clusterZ * clusterSizeCells, 0, grid.getHeight());
202 const int maxCellX = std::clamp((clusterX + 1) * clusterSizeCells, minCellX, grid.getWidth());
203 const int maxCellY = std::clamp((clusterZ + 1) * clusterSizeCells, minCellY, grid.getHeight());
204 BuildLayerRegion region{minCellX, minCellY, maxCellX, maxCellY, float(clusterX) * worldSize,
205 float(clusterZ) * worldSize, float(clusterX + 1) * worldSize,
206 float(clusterZ + 1) * worldSize};
207 auto built = stack.executeRegion(grid, points, region, orientation);
208 if (!built.ok()) return Result<IncrementalBuildDelta>::failure(built.status());
209 Record record{hash, std::move(built).takeValue()};
210 delta.changes_.push_back({clusterX, clusterZ, false, record.artifacts});
211 upserts.emplace_back(key, std::move(record));
212 }
213 for (const auto key : removals) cache_.erase(key);
214 for (auto& [key, record] : upserts) cache_.insert_or_assign(key, std::move(record));
215 return Result<IncrementalBuildDelta>::success(std::move(delta));
216}
217
218void IncrementalBuildExecutor::clear() { cache_.clear(); }
219int IncrementalBuildExecutor::getCachedClusterCount() const noexcept { return int(cache_.size()); }
220
222 const auto found = cache_.find(keyFor(clusterX, clusterZ));
223 if (found == cache_.end())
225 Diagnostic::error(DiagnosticCode::NotFound, "cluster is not cached", {}, {}, "procgen.incrementalBuild"));
226 return Result<BuildLayerExecution>::success(found->second.artifacts);
227}
228
229} // namespace eve::procgen
double value
bool & active
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
Stable, structured diagnostics shared by engine modules.
int column
std::array< std::uint8_t, 32 > hash
Definition Evpack.cpp:172
std::uint32_t key
std::uint64_t bytes
std::string name
graphics::Canvas * previous
std::shared_ptr< const std::vector< glm::vec2 > > points
int detail
bool found
int removed
std::uint32_t count
Cell cell
float size
Definition TreeMesh.cpp:156
uint32_t index
glm::vec3 point
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
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
Ordered, versioned executor for procedural tile and object build layers.
Result< BuildLayerExecution > executeRegion(const Grid2D &grid, const PointSet &points, const BuildLayerRegion &region, const PointSet *orientation=nullptr) const
Execute enabled layers for one half-open cluster region.
Intermediate 2D generation result. cells store semantic ids (see Semantic.h), not tile GIDs — convert...
Definition Grid2D.h:36
Ordered changes required to synchronize a scene-side cluster cache.
int getCount() const noexcept
Returns the count.
int getClusterX(int index) const noexcept
Returns the cluster x.
Result< BuildLayerExecution > getArtifacts(int index) const
Returns the artifacts.
bool isRemoved(int index) const noexcept
True when removed.
int getClusterZ(int index) const noexcept
Returns the cluster z.
Result< BuildLayerExecution > getCachedArtifacts(int clusterX, int clusterZ) const
Returns the cached artifacts.
int getCachedClusterCount() const noexcept
Returns the cached cluster count.
Result< IncrementalBuildDelta > update(const BuildLayerStack &stack, const Grid2D &grid, const PointSet &points, int clusterSizeCells, float cellSizeWorld, const PointSet *orientation=nullptr)
Rebuild only clusters whose layer definition, grid halo, point rows or orientation changed.
Script-friendly collection of attributed 3D samples.
Definition PointSet.h:59
constexpr uint32_t Empty
Definition Semantic.h:12
WidgetDesc grid(int columns, std::vector< WidgetDesc > children, std::string id)
Fixed-column grid; children are placed in source order.
Definition Widget.cpp:687
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
Definition Widget.cpp:679
WidgetDesc separator(std::string id)
Horizontal separator line.
Definition Widget.cpp:345
Half-open grid and world-space bounds used for one incremental cluster build.