载入中...
搜索中...
未找到
SceneCapabilities.cpp
浏览该文件的文档.
1#include "common/Capability.h"
4#include "common/SceneQuery.h"
5#include "scene/Scene.h"
6#include "scene/SceneHost.h"
7#include "scene/SceneObject.h"
9
10#include <glm/gtx/matrix_decompose.hpp>
11#include <glm/gtx/quaternion.hpp>
12
13#include <cstdint>
14#include <string>
15#include <unordered_map>
16#include <unordered_set>
17#include <utility>
18#include <vector>
19
20namespace eve::scene {
21namespace {
22
23template <class T>
24T *borrowSceneResult(eve::Result<T *> result) {
25 if (!result.ok()) return nullptr;
26 return std::move(result).takeValue();
27}
28
29SceneHost *hostByName(const std::string &name) {
30 if (name.empty()) return nullptr;
31 if (ecs::current()->getManager<SceneHost>() == nullptr) return nullptr;
32 auto view = ecs::View<SceneHost, SceneHost::Meta>();
33 for (auto it = view.begin(); it != view.end(); ++it) {
34 auto [meta] = *it;
35 if (meta->entity && meta->name == name) return meta->entity;
36 }
37 return nullptr;
38}
39
40SceneHost *hostAt(int index) {
41 if (index < 0 || ecs::current()->getManager<SceneHost>() == nullptr) return nullptr;
42 auto view = ecs::View<SceneHost, SceneHost::Meta>();
43 int i = 0;
44 for (auto it = view.begin(); it != view.end(); ++it, ++i) {
45 auto [meta] = *it;
46 if (i == index) return meta->entity;
47 }
48 return nullptr;
49}
50
51class SceneEntitySpatialProvider final : public eve::IEntitySpatialProvider {
52public:
53 eve::Result<eve::EntitySpatialPose> resolve(ecs::EntityHandle handle,
54 std::string_view bone) const override {
55 auto* object = dynamic_cast<SceneObject*>(ecs::try_get(handle));
56 if (!object)
57 return failure(eve::DiagnosticCode::NotFound, "entity is not owned by Scene", "entity");
58 if (!bone.empty())
60 "Scene root provider cannot resolve a bone or socket", "bone");
61 auto* host = hostByName(object->meta()->hostName);
62 if (!host) return failure(eve::DiagnosticCode::NotFound, "owning scene host is unavailable", "entity");
63 auto node = host->findById(object->meta()->nodeId);
66 glm::vec3 scale;
67 glm::quat rotation;
68 glm::vec3 translation;
69 glm::vec3 skew;
70 glm::vec4 perspective;
71 if (!glm::decompose(node.value()->world, scale, rotation, translation, skew, perspective))
72 return failure(eve::DiagnosticCode::Failed, "scene world transform could not be decomposed", "entity");
73 const glm::vec3 euler = glm::degrees(glm::eulerAngles(rotation));
74 return eve::Result<eve::EntitySpatialPose>::success({translation.x, translation.y, translation.z,
75 euler.x, euler.y, euler.z,
76 scale.x, scale.y, scale.z});
77 }
78
79private:
81 std::string path) {
84 }
85};
86
87SceneNodeInfo toInfo(SceneHost *host, const SceneNode &n) {
88 SceneNodeInfo info;
89 info.id = n.id;
90 info.name = n.name;
91 info.path = host->getPathById(n.id);
92 info.visible = n.visible;
93 info.x = n.x;
94 info.y = n.y;
95 info.z = n.z;
96 info.yaw = n.yaw;
97 info.pitch = n.pitch;
98 info.roll = n.roll;
99 info.sx = n.sx;
100 info.sy = n.sy;
101 info.sz = n.sz;
102 if (auto *p = host->getParentById(n.id)) info.parent = p->id;
103 for (int i = 0; i < host->getChildCountById(n.id); ++i) {
104 if (auto *c = host->getChildAtById(n.id, i)) info.children.push_back(c->id);
105 }
106 return info;
107}
108
109class SceneQueryImpl final : public eve::ISceneQuery {
110public:
111 SceneHost *host() const {
112 auto *s = eve::ModuleManager::getInstance<Scene>("Scene");
113 return s ? s->current() : nullptr;
114 }
115
116 std::string activeHost() const override {
117 auto *h = host();
118 return h ? h->getName() : std::string();
119 }
120
121 int hostCount() const override {
122 if (ecs::current()->getManager<SceneHost>() == nullptr) return 0;
123 auto view = ecs::View<SceneHost, SceneHost::Meta>();
124 int n = 0;
125 for (auto it = view.begin(); it != view.end(); ++it) ++n;
126 return n;
127 }
128
129 std::string hostNameAt(int index) const override {
130 auto *h = hostAt(index);
131 return h ? h->getName() : std::string();
132 }
133
134 int nodeCount() const override {
135 auto *h = host();
136 return h ? h->getNodeCount() : 0;
137 }
138
139 std::string rootId() const override {
140 auto *h = host();
141 auto *r = h ? h->getRoot() : nullptr;
142 return r ? r->id : std::string();
143 }
144
145 std::vector<SceneNodeInfo> nodes(int limit) const override {
146 return nodesOf(host() ? host()->getName() : std::string(), limit);
147 }
148
149 std::vector<SceneNodeInfo> nodesOf(const std::string &hostName, int limit) const override {
150 std::vector<SceneNodeInfo> out;
151 SceneHost *h = hostName.empty() ? host() : hostByName(hostName);
152 if (!h) return out;
153 h->walkDepthFirst([&](SceneHost *, int, SceneNode &n) {
154 if (static_cast<int>(out.size()) >= limit) return;
155 out.push_back(toInfo(h, n));
156 });
157 return out;
158 }
159
160 bool getNode(const std::string &id, SceneNodeInfo *out) const override {
161 auto *h = host();
162 return h ? getNodeIn(h->getName(), id, out) : false;
163 }
164
165 bool getNodeIn(const std::string &hostName, const std::string &id,
166 SceneNodeInfo *out) const override {
167 SceneHost *h = hostName.empty() ? host() : hostByName(hostName);
168 SceneNode *n = h ? borrowSceneResult(h->findById(id)) : nullptr;
169 if (!n || !out) return false;
170 *out = toInfo(h, *n);
171 return true;
172 }
173
174 bool setNodeTransform(const std::string &id, float x, float y, float z) override {
175 auto *h = host();
176 SceneNode *n = h ? borrowSceneResult(h->findById(id)) : nullptr;
177 if (!n) return false;
178 n->x = x;
179 n->y = y;
180 n->z = z;
181 h->markTransformDirty();
182 return true;
183 }
184
185 bool setNodeVisible(const std::string &id, bool visible) override {
186 auto *h = host();
187 SceneNode *n = h ? borrowSceneResult(h->findById(id)) : nullptr;
188 if (!n) return false;
189 n->visible = visible;
190 return true;
191 }
192
193 void syncTransforms() override { TransformSystem::updateAll(); }
194};
195
196class ProcgenSceneSinkImpl final : public eve::IProcgenSceneSink {
197public:
198 bool applyBatch(const std::string& batchId,
199 const std::vector<eve::ProcgenInstanceDesc>& instances) override {
200 if (batchId.empty()) return false;
201 SceneHost* host = hostByName(hostName(batchId));
202 if (!host) host = borrowSceneResult(SceneHost::createHost(hostName(batchId)));
203 if (!host) return false;
204
205 NodeDesc root;
206 root.id = "pcg-root";
207 root.key = "pcg-root";
208 root.name = batchId;
209 root.tags = {"pcg", "pcg.batch"};
210 root.children.reserve(instances.size());
211 for (const auto& instance : instances) {
212 NodeDesc child;
213 child.id = instance.id;
214 child.key = instance.id;
215 child.name = instance.asset;
216 child.x = instance.x;
217 child.y = instance.y;
218 child.z = instance.z;
219 child.yaw = instance.yaw;
220 child.sx = instance.scaleX;
221 child.sy = instance.scaleY;
222 child.sz = instance.scaleZ;
223 child.tags = {"pcg", "pcg.instance"};
224 if (!instance.asset.empty()) child.tags.push_back("pcg.asset:" + instance.asset);
225 root.children.push_back(std::move(child));
226 }
227 std::unordered_set<std::string> nextIds;
228 std::unordered_map<uint64_t, std::string> nextPointIds;
229 nextIds.reserve(instances.size());
230 nextPointIds.reserve(instances.size());
231 for (const auto& instance : instances) {
232 if (!nextIds.insert(instance.id).second) return false;
233 if (instance.sourcePointId != 0 && !nextPointIds.emplace(instance.sourcePointId, instance.id).second)
234 return false;
235 }
236 const auto previous = ids_.find(batchId);
237 const auto& previousIds = previous == ids_.end() ? emptyIds_ : previous->second;
238 Stats stats;
239 for (const auto& id : nextIds) {
240 if (previousIds.find(id) == previousIds.end()) ++stats.created;
241 else ++stats.reused;
242 }
243 for (const auto& id : previousIds) {
244 if (nextIds.find(id) == nextIds.end()) ++stats.removed;
245 }
246
247 host->setVisible(true);
248 host->setTreeReconcile(std::move(root));
250 counts_[batchId] = int(instances.size());
251 ids_[batchId] = std::move(nextIds);
252 instances_[batchId] = instances;
253 pointIds_[batchId] = std::move(nextPointIds);
254 revisions_[batchId] = revisions_[batchId] + 1;
255 stats_[batchId] = stats;
256 return true;
257 }
258
259 eve::Result<uint64_t> replaceBatch(const std::string& batchId, uint64_t targetRevision,
260 const std::vector<eve::ProcgenInstanceDesc>& instances) override {
261 const uint64_t currentRevision = batchRevision(batchId);
262 if (batchId.empty() || targetRevision == 0)
265 "procedural scene snapshot requires a batch id and non-zero target revision", "targetRevision"));
266 if (currentRevision >= targetRevision)
268 eve::DiagnosticCode::Conflict, "procedural scene snapshot revision is stale", "targetRevision"));
269 std::unordered_set<std::string> instanceIds;
270 std::unordered_set<uint64_t> pointIds;
271 instanceIds.reserve(instances.size());
272 pointIds.reserve(instances.size());
273 for (const auto& instance : instances) {
274 if (instance.id.empty() || instance.sourcePointId == 0 || !instanceIds.insert(instance.id).second ||
275 !pointIds.insert(instance.sourcePointId).second)
278 "procedural scene snapshot requires unique instance ids and source PointIds", "instances"));
279 }
280 if (!applyBatch(batchId, instances))
282 eve::DiagnosticCode::Failed, "procedural scene provider rejected snapshot commit", "batchId"));
283 revisions_[batchId] = targetRevision;
284 return eve::Result<uint64_t>::success(targetRevision);
285 }
286
287 eve::Result<uint64_t> replaceBatches(const std::vector<eve::ProcgenBatchSnapshot>& snapshots) override {
288 if (snapshots.empty())
291 "procedural scene transaction requires at least one snapshot", "snapshots"));
292
293 struct PreparedBatch {
294 const eve::ProcgenBatchSnapshot* snapshot = nullptr;
295 SceneHost* host = nullptr;
296 SceneHost::Tree tree;
297 std::unordered_set<std::string> ids;
298 std::unordered_map<uint64_t, std::string> pointIds;
299 std::vector<std::string> addedIds;
300 std::vector<std::string> reusedIds;
301 std::vector<std::string> removedIds;
302 Stats stats;
303 };
304 std::vector<PreparedBatch> prepared;
305 prepared.reserve(snapshots.size());
306 std::unordered_set<std::string> batchIds;
307 batchIds.reserve(snapshots.size());
308
309 for (const auto& snapshot : snapshots) {
310 if (snapshot.batchId.empty() || snapshot.targetRevision == 0)
313 "procedural scene transaction requires batch ids and non-zero revisions", "snapshots"));
314 if (!batchIds.insert(snapshot.batchId).second)
316 eve::DiagnosticCode::Conflict, "procedural scene transaction repeats a batch id", "snapshots"));
317 if (batchRevision(snapshot.batchId) >= snapshot.targetRevision)
320 "procedural scene transaction contains a stale revision", snapshot.batchId));
321
322 PreparedBatch batch;
323 batch.snapshot = &snapshot;
324 batch.ids.reserve(snapshot.instances.size());
325 batch.pointIds.reserve(snapshot.instances.size());
326 for (const auto& instance : snapshot.instances) {
327 if (instance.id.empty() || instance.sourcePointId == 0 || !batch.ids.insert(instance.id).second ||
328 !batch.pointIds.emplace(instance.sourcePointId, instance.id).second)
331 "procedural scene transaction requires unique instance ids and source PointIds",
332 snapshot.batchId));
333 }
334 const auto previous = ids_.find(snapshot.batchId);
335 const auto& previousIds = previous == ids_.end() ? emptyIds_ : previous->second;
336 for (const auto& id : batch.ids) {
337 if (previousIds.find(id) == previousIds.end()) {
338 ++batch.stats.created;
339 batch.addedIds.push_back(id);
340 } else {
341 ++batch.stats.reused;
342 batch.reusedIds.push_back(id);
343 }
344 }
345 for (const auto& id : previousIds) {
346 if (batch.ids.find(id) != batch.ids.end()) continue;
347 ++batch.stats.removed;
348 batch.removedIds.push_back(id);
349 }
350
351 batch.host = hostByName(hostName(snapshot.batchId));
352 NodeDesc root = makeRoot(snapshot.batchId, snapshot.instances);
353 auto tree = SceneHost::buildDetachedTree(batch.host ? &*batch.host->tree() : nullptr, std::move(root));
354 if (!tree) return eve::Result<uint64_t>::failure(tree.status());
355 batch.tree = std::move(tree).takeValue();
356 prepared.push_back(std::move(batch));
357 }
358
359 auto nextCounts = counts_;
360 auto nextIds = ids_;
361 auto nextInstances = instances_;
362 auto nextPointIds = pointIds_;
363 auto nextRevisions = revisions_;
364 auto nextStats = stats_;
365 for (const auto& batch : prepared) {
366 const auto& snapshot = *batch.snapshot;
367 nextCounts[snapshot.batchId] = int(snapshot.instances.size());
368 nextIds[snapshot.batchId] = batch.ids;
369 nextInstances[snapshot.batchId] = snapshot.instances;
370 nextPointIds[snapshot.batchId] = batch.pointIds;
371 nextRevisions[snapshot.batchId] = snapshot.targetRevision;
372 nextStats[snapshot.batchId] = batch.stats;
373 }
374
375 for (auto& batch : prepared) {
376 if (batch.host) continue;
377 batch.host = borrowSceneResult(SceneHost::createHost(hostName(batch.snapshot->batchId)));
378 if (!batch.host)
380 eve::DiagnosticCode::Failed, "procedural scene transaction could not allocate all target hosts",
381 batch.snapshot->batchId));
382 batch.host->setVisible(false);
383 }
384
385 for (auto& batch : prepared) {
386 *batch.host->tree() = std::move(batch.tree);
387 batch.host->setVisible(true);
388 }
389 counts_.swap(nextCounts);
390 ids_.swap(nextIds);
391 instances_.swap(nextInstances);
392 pointIds_.swap(nextPointIds);
393 revisions_.swap(nextRevisions);
394 stats_.swap(nextStats);
395 for (const auto& batch : prepared) {
396 for (const auto& id : batch.removedIds) batch.host->fireEvent("node_removed", id);
397 for (const auto& id : batch.addedIds) batch.host->fireEvent("node_added", id, "pcg-root");
398 for (const auto& id : batch.reusedIds) batch.host->fireEvent("node_changed", id, "pcg-root");
399 }
400 for (const auto& batch : prepared) TransformSystem::updateHost(batch.host);
401 return eve::Result<uint64_t>::success(static_cast<uint64_t>(prepared.size()));
402 }
403
404 eve::Result<uint64_t> applyDelta(const std::string& batchId,
405 const eve::ProcgenInstanceDelta& delta) override {
406 const auto current = revisions_.find(batchId);
407 if (batchId.empty() || current == revisions_.end())
409 eve::DiagnosticCode::NotFound, "procedural scene batch is not published", "batchId"));
410 if (delta.baseRevision == 0 || current->second != delta.baseRevision ||
411 delta.targetRevision != delta.baseRevision + 1)
413 eve::DiagnosticCode::Conflict, "procedural scene delta revision is stale", "baseRevision"));
414
415 std::unordered_map<std::string, eve::ProcgenInstanceDesc> staged;
416 for (const auto& instance : instances_.at(batchId)) staged.emplace(instance.id, instance);
417 std::unordered_map<uint64_t, std::string> stagedPointIds = pointIds_.at(batchId);
418 std::unordered_set<std::string> removedIds;
419 removedIds.reserve(delta.removedPointIds.size() + delta.removed.size());
420 for (const auto pointId : delta.removedPointIds) {
421 const auto found = stagedPointIds.find(pointId);
422 if (pointId == 0 || found == stagedPointIds.end() || !removedIds.insert(found->second).second)
424 eve::DiagnosticCode::Conflict, "procedural scene delta removes an unknown source PointId",
425 "removedPointIds"));
426 }
427 for (const auto& id : delta.removed)
428 if (id.empty() || !removedIds.insert(id).second)
429 return eve::Result<uint64_t>::failure(eve::Diagnostic::error(
430 eve::DiagnosticCode::Conflict, "procedural scene delta repeats a removed identity", "removed"));
431 for (const auto& id : removedIds) {
432 const auto found = staged.find(id);
433 if (found == staged.end())
435 eve::DiagnosticCode::Conflict, "procedural scene delta removes an unknown identity", "removed"));
436 if (found->second.sourcePointId != 0) stagedPointIds.erase(found->second.sourcePointId);
437 staged.erase(found);
438 }
439 for (const auto& instance : delta.updated) {
440 if (instance.id.empty() || instance.sourcePointId == 0)
443 "procedural scene delta updates require an id and source PointId", "updated"));
444 const auto identity = stagedPointIds.find(instance.sourcePointId);
445 if (identity == stagedPointIds.end())
447 eve::DiagnosticCode::Conflict, "procedural scene delta updates an unknown identity", "updated"));
448 if (identity->second != instance.id && staged.find(instance.id) != staged.end())
451 "procedural scene delta update renames to a duplicate identity", "updated"));
452 staged.erase(identity->second);
453 staged.emplace(instance.id, instance);
454 identity->second = instance.id;
455 }
456 for (const auto& instance : delta.added) {
457 if (instance.id.empty() || instance.sourcePointId == 0 ||
458 stagedPointIds.find(instance.sourcePointId) != stagedPointIds.end() ||
459 !staged.emplace(instance.id, instance).second)
461 eve::DiagnosticCode::Conflict, "procedural scene delta adds a duplicate identity", "added"));
462 stagedPointIds.emplace(instance.sourcePointId, instance.id);
463 }
464 if (!delta.targetPointOrder.empty() && !delta.targetOrder.empty())
467 "procedural scene delta cannot mix PointId and compatibility target orders", "targetPointOrder"));
468 std::vector<std::string> resolvedOrder;
469 if (!delta.targetPointOrder.empty()) {
470 resolvedOrder.reserve(delta.targetPointOrder.size());
471 std::unordered_set<uint64_t> orderedPointIds;
472 for (const auto pointId : delta.targetPointOrder) {
473 const auto found = stagedPointIds.find(pointId);
474 if (pointId == 0 || found == stagedPointIds.end() || !orderedPointIds.insert(pointId).second)
477 "procedural scene delta target order has an unknown or duplicate source PointId",
478 "targetPointOrder"));
479 resolvedOrder.push_back(found->second);
480 }
481 } else {
482 resolvedOrder = delta.targetOrder;
483 }
484 if (staged.size() != resolvedOrder.size())
486 eve::DiagnosticCode::Conflict, "procedural scene delta target order is incomplete", "targetOrder"));
487 std::vector<eve::ProcgenInstanceDesc> target;
488 target.reserve(resolvedOrder.size());
489 for (const auto& id : resolvedOrder) {
490 auto found = staged.find(id);
491 if (found == staged.end())
494 "procedural scene delta target order has unknown identity", "targetOrder"));
495 target.push_back(found->second);
496 staged.erase(found);
497 }
498 if (!staged.empty())
501 "procedural scene delta target order repeats an identity", "targetOrder"));
502 return replaceBatch(batchId, delta.targetRevision, target);
503 }
504
505 uint64_t batchRevision(const std::string& batchId) const override {
506 const auto found = revisions_.find(batchId);
507 return found == revisions_.end() ? 0 : found->second;
508 }
509
510 bool removeBatch(const std::string& batchId) override {
511 SceneHost* host = hostByName(hostName(batchId));
512 if (!host) return false;
513 NodeDesc root;
514 root.id = "pcg-root";
515 root.key = "pcg-root";
516 root.name = batchId;
517 root.tags = {"pcg", "pcg.batch"};
518 host->setTree(std::move(root));
519 host->setVisible(false);
520 Stats stats;
521 const auto ids = ids_.find(batchId);
522 if (ids != ids_.end()) stats.removed = int(ids->second.size());
523 stats_[batchId] = stats;
524 counts_.erase(batchId);
525 ids_.erase(batchId);
526 instances_.erase(batchId);
527 pointIds_.erase(batchId);
528 revisions_.erase(batchId);
529 return true;
530 }
531
532 eve::Result<uint64_t> removeBatches(const std::vector<std::string>& batchIds) override {
533 return removeBatchesCoordinated(batchIds, [] { return eve::Result<void>::success(); });
534 }
535
536 eve::Result<uint64_t> removeBatchesCoordinated(
537 const std::vector<std::string>& batchIds,
538 const std::function<eve::Result<void>()>& commitPreparedOwner) override {
539 if (batchIds.empty())
542 "procedural scene removal transaction requires at least one batch id", "batchIds"));
543 struct PreparedRemoval {
544 std::string batchId;
545 SceneHost* host = nullptr;
546 SceneHost::Tree tree;
547 std::vector<std::string> removedIds;
548 Stats stats;
549 };
550 std::vector<PreparedRemoval> prepared;
551 prepared.reserve(batchIds.size());
552 std::unordered_set<std::string> uniqueBatchIds;
553 uniqueBatchIds.reserve(batchIds.size());
554 for (const auto& batchId : batchIds) {
555 const auto ids = ids_.find(batchId);
556 if (batchId.empty() || !uniqueBatchIds.insert(batchId).second)
559 "procedural scene removal transaction requires unique non-empty batch ids", "batchIds"));
560 SceneHost* host = hostByName(hostName(batchId));
561 if (ids == ids_.end() || !host)
563 eve::DiagnosticCode::NotFound, "procedural scene removal transaction contains an unpublished batch",
564 batchId));
565 PreparedRemoval removal;
566 removal.batchId = batchId;
567 removal.host = host;
568 removal.removedIds.assign(ids->second.begin(), ids->second.end());
569 std::sort(removal.removedIds.begin(), removal.removedIds.end());
570 removal.stats.removed = int(ids->second.size());
571 auto tree = SceneHost::buildDetachedTree(&*host->tree(), makeRoot(batchId, {}));
572 if (!tree) return eve::Result<uint64_t>::failure(tree.status());
573 removal.tree = std::move(tree).takeValue();
574 prepared.push_back(std::move(removal));
575 }
576
577 auto nextCounts = counts_;
578 auto nextIds = ids_;
579 auto nextInstances = instances_;
580 auto nextPointIds = pointIds_;
581 auto nextRevisions = revisions_;
582 auto nextStats = stats_;
583 for (const auto& removal : prepared) {
584 nextCounts.erase(removal.batchId);
585 nextIds.erase(removal.batchId);
586 nextInstances.erase(removal.batchId);
587 nextPointIds.erase(removal.batchId);
588 nextRevisions.erase(removal.batchId);
589 nextStats[removal.batchId] = removal.stats;
590 }
591 if (!commitPreparedOwner)
593 eve::DiagnosticCode::InvalidArgument, "procedural scene removal transaction requires a commit",
594 "commitPreparedOwner"));
595 auto ownerCommit = commitPreparedOwner();
596 if (!ownerCommit.ok()) return eve::Result<uint64_t>::failure(ownerCommit.status());
597 for (auto& removal : prepared) {
598 *removal.host->tree() = std::move(removal.tree);
599 removal.host->setVisible(false);
600 }
601 counts_.swap(nextCounts);
602 ids_.swap(nextIds);
603 instances_.swap(nextInstances);
604 pointIds_.swap(nextPointIds);
605 revisions_.swap(nextRevisions);
606 stats_.swap(nextStats);
607 for (const auto& removal : prepared)
608 for (const auto& id : removal.removedIds) removal.host->fireEvent("node_removed", id);
609 return eve::Result<uint64_t>::success(static_cast<uint64_t>(prepared.size()));
610 }
611
612 int instanceCount(const std::string& batchId) const override {
613 const auto found = counts_.find(batchId);
614 return found == counts_.end() ? 0 : found->second;
615 }
616 int lastCreatedCount(const std::string& batchId) const override {
617 const auto found = stats_.find(batchId);
618 return found == stats_.end() ? 0 : found->second.created;
619 }
620 int lastReusedCount(const std::string& batchId) const override {
621 const auto found = stats_.find(batchId);
622 return found == stats_.end() ? 0 : found->second.reused;
623 }
624 int lastRemovedCount(const std::string& batchId) const override {
625 const auto found = stats_.find(batchId);
626 return found == stats_.end() ? 0 : found->second.removed;
627 }
628
629private:
630 struct Stats {
631 int created = 0;
632 int reused = 0;
633 int removed = 0;
634 };
635 static NodeDesc makeRoot(const std::string& batchId, const std::vector<eve::ProcgenInstanceDesc>& instances) {
636 NodeDesc root;
637 root.id = "pcg-root";
638 root.key = "pcg-root";
639 root.name = batchId;
640 root.tags = {"pcg", "pcg.batch"};
641 root.children.reserve(instances.size());
642 for (const auto& instance : instances) {
643 NodeDesc child;
644 child.id = instance.id;
645 child.key = instance.id;
646 child.name = instance.asset;
647 child.x = instance.x;
648 child.y = instance.y;
649 child.z = instance.z;
650 child.yaw = instance.yaw;
651 child.sx = instance.scaleX;
652 child.sy = instance.scaleY;
653 child.sz = instance.scaleZ;
654 child.tags = {"pcg", "pcg.instance"};
655 if (!instance.asset.empty()) child.tags.push_back("pcg.asset:" + instance.asset);
656 root.children.push_back(std::move(child));
657 }
658 return root;
659 }
660 static std::string hostName(const std::string& batchId) { return "__pcg/" + batchId; }
661 const std::unordered_set<std::string> emptyIds_;
662 std::unordered_map<std::string, int> counts_;
663 std::unordered_map<std::string, std::unordered_set<std::string>> ids_;
664 std::unordered_map<std::string, std::vector<eve::ProcgenInstanceDesc>> instances_;
665 std::unordered_map<std::string, std::unordered_map<uint64_t, std::string>> pointIds_;
666 std::unordered_map<std::string, uint64_t> revisions_;
667 std::unordered_map<std::string, Stats> stats_;
668};
669
670} // namespace
671
673 static SceneQueryImpl impl;
674 static ProcgenSceneSinkImpl procgenSink;
675 static SceneEntitySpatialProvider spatialProvider;
676 eve::cap::provide<eve::ISceneQuery>(&impl);
677 eve::cap::provide<eve::IProcgenSceneSink>(&procgenSink);
678 eve::cap::addListener<eve::IEntitySpatialProvider>(&spatialProvider);
679}
680
681} // namespace eve::scene
LogicalId target
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
const std::string & s
std::vector< BuildingInstanceSnapshot > instances
void * impl
glm::vec4 p[6]
Optional ECS-entity to world-pose capability.
std::string message
DiagnosticCode code
std::uint32_t instanceCount
glm::uvec4 ids
glm::vec3 n
Definition Grass.cpp:63
double r
std::int32_t second
std::int32_t c
int h
std::array< float, 4 > rotation
std::array< float, 3 > scale
std::uint32_t bone
std::string name
std::vector< BvhNode > nodes
graphics::Canvas * previous
OnnxTransferStats stats
Definition OnnxGpgpu.cpp:56
std::string error
Definition Package.cpp:60
std::array< std::uint64_t, kPixelChunkSize *kPixelChunkSize > updated
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
PrimitiveHandle handle
glm::mat4 view
const RoadNode * node
bool found
int created
int removed
int reused
double current
std::string string
bool visible
Json object
int limit
Definition TreeMesh.cpp:164
UIHostHandle host
uint32_t index
std::size_t at
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
One optional domain provider capable of resolving selected ECS entities.
Optional scene consumer for procedural instance batches.
Scene graph query/mutation surface (provided by the scene module).
Definition SceneQuery.h:30
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
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:255
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static eve::Result< Tree > buildDetachedTree(const Tree *previous, NodeDesc root)
Builds a detached replacement tree without mutating a host or firing callbacks.
Definition NodeDesc.cpp:296
static eve::Result< SceneHost * > createHost(const std::string &name="")
Creates an ECS-owned scene host with an explicit Result contract.
static void updateHost(SceneHost *host)
Updates host.
static void updateAll()
Updates all.
eve::Diagnostic Diagnostic
eve::Result< T > Result
void registerSceneCapabilities()
Registers scene capabilities.
WidgetDesc child(std::string id, std::vector< WidgetDesc > children, float width, float height)
Scrollable child region with an explicit size.
Definition Widget.cpp:635
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
One complete cell snapshot participating in an atomic multi-batch publication.
Identity-based, revision-checked mutation of one procedural scene batch.
std::vector< uint64_t > removedPointIds
Stable source PointIds removed from the target batch.
std::vector< std::string > targetOrder
Compatibility target order addressed directly by Scene instance id.
std::vector< std::string > removed
Compatibility removals addressed directly by Scene instance id.
std::vector< uint64_t > targetPointOrder
Exact target order addressed by stable source PointId.
std::vector< WidgetDesc > children
Definition Widget.h:111
std::string key
Reconciliation key; defaults to id when empty.
Definition Widget.h:19
std::string id
Definition Widget.h:17
glm::uvec4 info