载入中...
搜索中...
未找到
BiomeRulesEditor.cpp
浏览该文件的文档.
2
4#include "procgen/PointSet.h"
5
6#include <algorithm>
7#include <cmath>
8#include <cstdint>
9#include <utility>
10
11namespace eve::biome_editor {
12namespace {
13
14template <class T = void>
15biome_editing::Result<T> editorError(biome_editing::EditorStatus status, std::string rule, std::string message) {
16 return eve::editing::failed<T>(status, biome_editing::RuleId(std::move(rule)), std::move(message));
17}
18
19} // namespace
20
21BiomeRulesEditor::SpatialResolver::SpatialResolver(procgen::SpatialData* forest, procgen::SpatialData* clearing)
22 : forest_(forest), clearing_(clearing) {}
23
24biome_editing::Result<procgen::SpatialData*> BiomeRulesEditor::SpatialResolver::resolve(
25 const std::string& asset) const {
26 if (asset == "asset://preview/forest.spatial")
27 return eve::editing::applied<procgen::SpatialData*>(forest_);
28 if (asset == "asset://preview/clearing.spatial")
29 return eve::editing::applied<procgen::SpatialData*>(clearing_);
30 return editorError<procgen::SpatialData*>(biome_editing::EditorStatus::NotFound, "editor.biome.spatial",
31 "Unknown biome spatial asset");
32}
33
34BiomeRulesEditor::BiomeRulesEditor(std::string targetId)
35 : target_(std::move(targetId)),
36 authority_(&target_),
37 transactions_(&authority_),
38 forestDomain_(procgen::SpatialData::box(0.0f, 0.0f, 0.0f, 4.0f, 0.0f, 4.0f)),
39 clearingDomain_(procgen::SpatialData::box(0.0f, 0.0f, 0.0f, 1.5f, 0.0f, 1.5f)),
40 resolver_(&forestDomain_, &clearingDomain_) {
41 seedPreviewDocument();
42 auto previewed = refreshPreview();
43 if (!previewed.ok())
44 previewed.ignore("biome editor keeps an empty PointSet when the seeded preview is rejected");
45}
46
47void BiomeRulesEditor::seedPreviewDocument() {
49 assets.push_back(biome_editing::EditorValue::Object{{"id", std::string("oak")},
50 {"asset", std::string("models/oak.prefab")},
51 {"weight", 1.0},
52 {"minScale", 1.0},
53 {"maxScale", 1.0},
54 {"randomYaw", false}});
56 layers.push_back(biome_editing::EditorValue::Object{{"id", std::string("forest")},
57 {"name", std::string("forest")},
58 {"spatial", std::string("asset://preview/forest.spatial")},
59 {"priority", std::int64_t{1}},
60 {"density", 1.0},
61 {"assets", std::move(assets)}});
63 content["layers"] = std::move(layers);
64 content["exclusions"] = biome_editing::EditorValue::Array{};
66 root["schemaVersion"] = std::int64_t{1};
67 root["content"] = biome_editing::EditorValue(std::move(content));
68 auto loaded = target_.loadSnapshot(biome_editing::EditorValue(std::move(root)));
69 if (!loaded.ok()) loaded.ignore("biome editor keeps defaults when the preview snapshot is rejected");
70}
71
73 editor::EditorWorkspace candidate = workspace;
74 struct Panel {
75 const char* id;
76 const char* title;
77 const char* region;
78 const char* context;
79 int order;
80 };
81 constexpr Panel panels[] = {
82 {"biome.layers", "Layers", "left", "list", 100},
83 {"biome.preview", "World Preview", "center", "preview", 100},
84 {"biome.inspector", "Biome Inspector", "right", "inspector", 100},
85 {"biome.assets", "Layer Assets", "bottom", "assets", 100},
86 };
87 for (const auto& panel : panels) {
88 if (!candidate.registerPanel(panel.id, panel.title, panel.region, panel.order) ||
89 !candidate.setPanelCapability(panel.id, "biome.rules") ||
90 !candidate.setPanelContext(panel.id, panel.context))
91 return editorError(biome_editing::EditorStatus::Rejected, "editor.biome.workspace-conflict",
92 "Could not install the biome workspace composition");
93 }
94 if (!candidate.activatePanel("biome.preview"))
95 return editorError(biome_editing::EditorStatus::Rejected, "editor.biome.workspace-activate",
96 "Could not activate the biome preview panel");
97 workspace = std::move(candidate);
98 return eve::editing::applied<void>();
99}
100
101editor::SelectionSnapshot BiomeRulesEditor::selection() const {
103 snapshot.channel = "biome";
105 item.domain = editor::SelectionDomain::Asset;
106 item.target = editor::TargetId(target_.targetId());
107 item.item = editor::StableId(selectedId_);
108 item.type = selectedType_;
109 snapshot.items.push_back(item);
110 snapshot.primary = item;
111 return snapshot;
112}
113
114biome_editing::Result<void> BiomeRulesEditor::commit(
116 if (!operation.ok())
119 spec.id = editor::TransactionId("biome.rules.tx." + std::to_string(++txSequence_));
120 spec.label = std::move(label);
121 spec.target = editor::TargetId(target_.targetId());
122 spec.baseRevision = target_.revision();
123 auto begun = transactions_.begin(std::move(spec));
124 if (!begun.ok())
125 return editorError(begun.code(), "editor.biome.begin", "Could not begin the biome transaction");
126 auto appended = transactions_.append(std::move(operation).takeValue());
127 if (!appended.ok()) {
128 auto discarded = transactions_.discard();
129 if (!discarded.ok()) discarded.ignore("pending biome transaction already inactive");
130 return biome_editing::Result<void>::failure(appended.status());
131 }
132 auto committed = transactions_.commit();
133 if (!committed.ok())
134 return biome_editing::Result<void>::failure(committed.status());
135 return refreshPreview();
136}
137
138biome_editing::Result<void> BiomeRulesEditor::refreshPreview() {
139 auto published = runtime_.publish(target_, resolver_);
140 if (!published.ok())
141 return biome_editing::Result<void>::failure(published.status());
142 auto generated = runtime_.preview(&forestDomain_, spacing_, seed_, 0.0f, runtime_.revision());
143 if (!generated.ok())
144 return biome_editing::Result<void>::failure(generated.status());
145 std::vector<PreviewPoint> next;
146 const auto& points = generated.value();
147 if (points) {
148 next.reserve(static_cast<std::size_t>(points->getCount()));
149 for (int i = 0; i < points->getCount(); ++i) {
150 PreviewPoint point;
151 point.x = points->getX(i);
152 point.z = points->getZ(i);
153 point.asset = points->getStringAttribute(i, "asset", {});
154 next.push_back(std::move(point));
155 }
156 }
157 points_ = std::move(next);
158 previewRevision_ = target_.revision();
159 return eve::editing::applied<void>();
160}
161
163 bool found = false;
164 for (const auto& layer : target_.layers())
165 if (layer.id.value() == id) found = true;
166 if (!found)
167 return editorError(biome_editing::EditorStatus::NotFound, "editor.biome.layer", "Biome layer was not found");
168 selectedId_ = std::move(id);
169 selectedType_ = "biome.layer";
170 return eve::editing::applied<void>();
171}
172
174 bool found = false;
175 for (const auto& layer : target_.layers())
176 for (const auto& asset : layer.assets)
177 if (asset.id.value() == id) found = true;
178 if (!found)
179 return editorError(biome_editing::EditorStatus::NotFound, "editor.biome.asset", "Biome asset was not found");
180 selectedId_ = std::move(id);
181 selectedType_ = "biome.asset";
182 return eve::editing::applied<void>();
183}
184
186 if (selectedType_ != "biome.layer")
187 return editorError(biome_editing::EditorStatus::Rejected, "editor.biome.selection",
188 "A layer must be selected to change density");
189 const auto previous = points_;
190 const auto revision = previewRevision_;
191 auto applied = commit(target_.makeSet(selection(), biome_editing::PropertyPath("layer.density"),
193 biome_editing::PropertySetMode::Absolute),
194 "Set layer density");
195 if (!applied.ok()) {
196 points_ = previous;
197 previewRevision_ = revision;
198 }
199 return applied;
200}
201
203 if (selectedType_ != "biome.layer")
204 return editorError(biome_editing::EditorStatus::Rejected, "editor.biome.selection",
205 "A layer must be selected to change priority");
206 return commit(target_.makeSet(selection(), biome_editing::PropertyPath("layer.priority"),
208 biome_editing::PropertySetMode::Absolute),
209 "Set layer priority");
210}
211
213 if (selectedType_ != "biome.asset")
214 return editorError(biome_editing::EditorStatus::Rejected, "editor.biome.selection",
215 "An asset must be selected to change weight");
216 return commit(target_.makeSet(selection(), biome_editing::PropertyPath("asset.weight"),
217 biome_editing::EditorValue(weight), biome_editing::PropertySetMode::Absolute),
218 "Set asset weight");
219}
220
223 layer.id = biome_editing::ObjectId(std::move(id));
224 layer.name = std::move(name);
225 layer.spatialAsset = "asset://preview/forest.spatial";
226 layer.density = 1.0f;
227 auto created = commit(target_.makeCreateLayer(layer), "Create layer");
228 if (!created.ok()) return created;
229 return selectLayer(layer.id.value());
230}
231
233 if (selectedType_ != "biome.layer")
234 return editorError(biome_editing::EditorStatus::Rejected, "editor.biome.selection",
235 "A layer must be selected to delete it");
236 auto deleted = commit(target_.makeDeleteLayer(biome_editing::ObjectId(selectedId_)), "Delete layer");
237 if (!deleted.ok()) return deleted;
238 if (!target_.layers().empty()) return selectLayer(target_.layers().front().id.value());
239 selectedId_.clear();
240 return eve::editing::applied<void>();
241}
242
244 const auto* layer = selectedLayer();
245 if (!layer)
246 return editorError(biome_editing::EditorStatus::Rejected, "editor.biome.selection",
247 "A layer must be selected to add an asset");
249 value.id = biome_editing::ObjectId(std::move(id));
250 value.asset = std::move(asset);
251 auto created = commit(target_.makeCreateAsset(layer->id, value), "Create asset");
252 if (!created.ok()) return created;
253 return selectAsset(value.id.value());
254}
255
257 if (selectedType_ != "biome.asset")
258 return editorError(biome_editing::EditorStatus::Rejected, "editor.biome.selection",
259 "An asset must be selected to delete it");
260 return commit(target_.makeDeleteAsset(biome_editing::ObjectId(selectedId_)), "Delete asset");
261}
262
264 auto next = target_.exclusions();
265 next.push_back(std::move(spatialAsset));
266 const auto previous = points_;
267 const auto revision = previewRevision_;
268 auto applied = commit(target_.makeSetExclusions(std::move(next)), "Add exclusion");
269 if (!applied.ok()) {
270 points_ = previous;
271 previewRevision_ = revision;
272 }
273 return applied;
274}
275
277 auto next = target_.exclusions();
278 std::erase(next, spatialAsset);
279 return commit(target_.makeSetExclusions(std::move(next)), "Remove exclusion");
280}
281
283 seed_ = seed;
284 return refreshPreview();
285}
286
288 if (!std::isfinite(spacing) || spacing <= 0.0f)
289 return editorError(biome_editing::EditorStatus::Rejected, "editor.biome.spacing",
290 "Biome preview spacing must be positive");
291 spacing_ = spacing;
292 return refreshPreview();
293}
294
296 auto result = transactions_.undo();
297 if (!result.ok()) return result;
298 auto previewed = refreshPreview();
299 if (!previewed.ok())
301 return result;
302}
303
305 auto result = transactions_.redo();
306 if (!result.ok()) return result;
307 auto previewed = refreshPreview();
308 if (!previewed.ok())
310 return result;
311}
312
313const biome_editing::BiomeLayerValue* BiomeRulesEditor::selectedLayer() const {
314 if (selectedType_ == "biome.layer") {
315 for (const auto& layer : target_.layers())
316 if (layer.id.value() == selectedId_) return &layer;
317 }
318 if (selectedType_ == "biome.asset") {
319 for (const auto& layer : target_.layers())
320 for (const auto& asset : layer.assets)
321 if (asset.id.value() == selectedId_) return &layer;
322 }
323 if (!target_.layers().empty()) return &target_.layers().front();
324 return nullptr;
325}
326
327const biome_editing::BiomeAssetValue* BiomeRulesEditor::assetAt(int index) const {
328 const auto* layer = selectedLayer();
329 if (!layer || index < 0 || static_cast<std::size_t>(index) >= layer->assets.size()) return nullptr;
330 return &layer->assets[static_cast<std::size_t>(index)];
331}
332
333std::string BiomeRulesEditor::layerId(int index) const {
334 if (index < 0 || static_cast<std::size_t>(index) >= target_.layers().size()) return {};
335 return target_.layers()[static_cast<std::size_t>(index)].id.value();
336}
337std::string BiomeRulesEditor::layerName(int index) const {
338 if (index < 0 || static_cast<std::size_t>(index) >= target_.layers().size()) return {};
339 return target_.layers()[static_cast<std::size_t>(index)].name;
340}
342 if (index < 0 || static_cast<std::size_t>(index) >= target_.layers().size()) return 0.0f;
343 return target_.layers()[static_cast<std::size_t>(index)].density;
344}
346 if (index < 0 || static_cast<std::size_t>(index) >= target_.layers().size()) return 0;
347 return target_.layers()[static_cast<std::size_t>(index)].priority;
348}
350 return selectedType_ == "biome.layer" && layerId(index) == selectedId_;
351}
352
354 const auto* layer = selectedLayer();
355 return layer ? static_cast<int>(layer->assets.size()) : 0;
356}
357std::string BiomeRulesEditor::assetId(int index) const {
358 const auto* asset = assetAt(index);
359 return asset ? asset->id.value() : std::string{};
360}
361std::string BiomeRulesEditor::assetRef(int index) const {
362 const auto* asset = assetAt(index);
363 return asset ? asset->asset : std::string{};
364}
366 const auto* asset = assetAt(index);
367 return asset ? asset->weight : 0.0f;
368}
370 const auto* asset = assetAt(index);
371 return asset && selectedType_ == "biome.asset" && asset->id.value() == selectedId_;
372}
373
375 if (index < 0 || static_cast<std::size_t>(index) >= target_.exclusions().size()) return {};
376 return target_.exclusions()[static_cast<std::size_t>(index)];
377}
378
380 if (index < 0 || static_cast<std::size_t>(index) >= points_.size()) return 0.0f;
381 return points_[static_cast<std::size_t>(index)].x;
382}
384 if (index < 0 || static_cast<std::size_t>(index) >= points_.size()) return 0.0f;
385 return points_[static_cast<std::size_t>(index)].z;
386}
387std::string BiomeRulesEditor::pointAsset(int index) const {
388 if (index < 0 || static_cast<std::size_t>(index) >= points_.size()) return {};
389 return points_[static_cast<std::size_t>(index)].asset;
390}
391
392} // namespace eve::biome_editor
ActionParameterOperation operation
double value
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
UI-neutral biome editor: workspace, PointSet preview, undo.
int priority
std::string title
std::string label
std::string message
wgpu::PopErrorScopeStatus status
std::string name
std::vector< std::int32_t > order
graphics::Canvas * previous
TileLayer * layer
std::string id
Definition PlayHost.cpp:108
std::uint32_t seed
Definition PointSet.cpp:807
std::uint32_t targetId
std::shared_ptr< const std::vector< glm::vec2 > > points
bool found
int created
int spacing
uint32_t index
const VegetationPresetContext & context
glm::vec3 point
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 failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
Result< void > publish(const BiomeDocumentTarget &, const IBiomeSpatialResolver &)
Publish.
Revision revision() const
Revision.
Result< std::unique_ptr< procgen::PointSet > > preview(procgen::SpatialData *domain, float spacing, std::uint32_t seed, float jitter, Revision expectedRevision)
Generate a PointSet from a previously published candidate.
Result< DomainOperation > makeCreateLayer(const BiomeLayerValue &) const
Make create layer.
const std::vector< std::string > & exclusions() const
Exclusions.
const std::vector< BiomeLayerValue > & layers() const
Layers.
Result< void > loadSnapshot(const EditorValue &)
Loads snapshot.
Result< DomainOperation > makeSet(const SelectionSnapshot &, const PropertyPath &, const EditorValue &, PropertySetMode) const override
Make set.
Result< DomainOperation > makeDeleteLayer(const ObjectId &) const
Make delete layer.
Result< DomainOperation > makeDeleteAsset(const ObjectId &) const
Make delete asset.
Result< DomainOperation > makeSetExclusions(std::vector< std::string >) const
Make set exclusions.
TargetId targetId() const override
Target id.
Definition BiomeTarget.h:74
Result< DomainOperation > makeCreateAsset(const ObjectId &layer, const BiomeAssetValue &) const
Make create asset.
std::string assetId(int index) const
Asset id.
int layerPriority(int index) const
Layer priority.
bool isLayerSelected(int index) const
True when layer selected.
biome_editing::Result< void > removeExclusion(std::string spatialAsset)
biome_editing::Result< void > setSeed(std::uint32_t seed)
std::string layerName(int index) const
Layer name.
biome_editing::Result< void > createAsset(std::string id, std::string asset)
std::uint32_t seed() const noexcept
Seed.
biome_editing::Result< void > addExclusion(std::string spatialAsset)
std::string exclusionAsset(int index) const
Exclusion asset.
float pointX(int index) const
Point x.
float pointZ(int index) const
Point z.
biome_editing::Result< void > createLayer(std::string id, std::string name)
biome_editing::Result< void > configureWorkspace(editor::EditorWorkspace &workspace) const
Install Layers / Preview / Inspector / Assets panels.
biome_editing::Result< void > selectLayer(std::string id)
float spacing() const noexcept
Spacing.
float assetWeight(int index) const
Asset weight.
biome_editing::Result< editor::TransactionReceipt > undo()
float layerDensity(int index) const
Layer density.
std::uint64_t revision() const noexcept
Revision.
std::string pointAsset(int index) const
Point asset.
biome_editing::Result< void > setAssetWeight(double weight)
bool isAssetSelected(int index) const
True when asset selected.
std::string layerId(int index) const
Layer id.
std::string assetRef(int index) const
Asset ref.
biome_editing::Result< void > setSpacing(float spacing)
biome_editing::Result< void > selectAsset(std::string id)
biome_editing::Result< void > deleteSelectedAsset()
biome_editing::Result< editor::TransactionReceipt > redo()
biome_editing::Result< void > setLayerDensity(double density)
biome_editing::Result< void > deleteSelectedLayer()
biome_editing::Result< void > setLayerPriority(int priority)
std::uint64_t revision() const override
Return the current revision.
Dotted property path (layout.size) compared and persisted as text.
Definition EditingIds.h:78
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
Composition model shared by project-specific developer and runtime editors.
bool setPanelContext(const std::string &id, const std::string &context)
Attach a semantic input context such as scene, graph, or timeline.
Result< WorkspacePanelDescriptor > registerPanel(WorkspacePanelDescriptor descriptor)
Register a checked owning panel descriptor.
Result< WorkspacePanelDescriptor > activatePanel(const StableId &id)
Activate a visible panel with structured not-found/rejected results.
bool setPanelCapability(const std::string &id, const std::string &capability)
Attach a capability requirement used by the host when filtering panels.
Result< TransactionReceipt > commit() override
Preflight and commit all active operations through the authority.
Result< TransactionId > begin(TransactionSpec specification) override
Begin a transaction with a stable id and base revision.
Result< TransactionReceipt > undo() override
Compensate the most recent committed transaction.
Result< void > discard() override
Discard the active, not-yet-committed operation list.
Result< TransactionReceipt > redo() override
Reapply the most recently compensated transaction.
Result< void > append(DomainOperation operation) override
Append an operation to the active transaction without applying it.
editing::Status EditorStatus
Definition BiomeTarget.h:23
editing::ObjectId ObjectId
Definition BiomeTarget.h:30
editing::RuleId RuleId
Definition BiomeTarget.h:40
editing::Value EditorValue
Definition BiomeTarget.h:24
eve::editing::StableId StableId
Definition EditorIds.h:36
eve::editing::TargetId TargetId
Definition EditorIds.h:29
eve::editing::TransactionId TransactionId
Definition EditorIds.h:34
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
Stable weighted asset entry in one biome layer.
Definition BiomeTarget.h:45
Stable authored biome layer referencing a spatial asset.
Definition BiomeTarget.h:51
SelectionItem public API.
SelectionSnapshot public API.
std::vector< SelectionItem > items
std::optional< SelectionItem > primary
TransactionSpec public API.