载入中...
搜索中...
未找到
SceneHost.cpp
浏览该文件的文档.
1#include "scene/SceneHost.h"
2
3#include "scene/NodeDesc.h"
4
5#include <utility>
6#include <vector>
7
8namespace eve::scene {
9namespace {
10
11uint32_t g_anonHostSeq = 0;
12
13template <class T>
14T *borrowSceneResult(eve::Result<T *> result) {
15 if (!result.ok()) return nullptr;
16 return std::move(result).takeValue();
17}
18
19bool isAncestor(const SceneHost::Tree &tree, int ancestor, int node) {
20 for (int p = node; p >= 0; p = tree.nodes[size_t(p)].parent) {
21 if (p == ancestor) return true;
22 }
23 return false;
24}
25
26void unlinkFromParent(SceneHost::Tree &tree, int childIndex) {
27 SceneNode &child = tree.nodes[size_t(childIndex)];
28 const int parent = child.parent;
29 if (parent < 0) return;
30 SceneNode &p = tree.nodes[size_t(parent)];
31 if (p.firstChild == childIndex) {
32 p.firstChild = child.nextSibling;
33 } else {
34 for (int c = p.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
35 if (tree.nodes[size_t(c)].nextSibling == childIndex) {
36 tree.nodes[size_t(c)].nextSibling = child.nextSibling;
37 break;
38 }
39 }
40 }
41 child.nextSibling = -1;
42 child.parent = -1;
43}
44
45void linkAsLastChild(SceneHost::Tree &tree, int parentIndex, int childIndex) {
46 SceneNode &parent = tree.nodes[size_t(parentIndex)];
47 SceneNode &child = tree.nodes[size_t(childIndex)];
48 child.parent = parentIndex;
49 child.nextSibling = -1;
50 if (parent.firstChild < 0) {
51 parent.firstChild = childIndex;
52 return;
53 }
54 int c = parent.firstChild;
55 while (tree.nodes[size_t(c)].nextSibling >= 0) c = tree.nodes[size_t(c)].nextSibling;
56 tree.nodes[size_t(c)].nextSibling = childIndex;
57}
58
60bool setLink(SceneNode &n, int kind, void *target, int syncMode) {
61 for (auto &l : n.links) {
62 if (l.kind == kind) {
63 l.target = target;
64 l.syncMode = syncMode;
65 return true;
66 }
67 }
68 n.links.push_back(SceneLink{kind, target, syncMode});
69 return true;
70}
71
72void forEachDepthFirstImpl(SceneHost *host, int nodeIndex, void (*fn)(SceneHost *, int, void *),
73 void *user) {
74 if (!host || nodeIndex < 0) return;
75 fn(host, nodeIndex, user);
76 auto t = host->tree();
77 for (int c = t->nodes[size_t(nodeIndex)].firstChild; c >= 0;
78 c = t->nodes[size_t(c)].nextSibling) {
79 forEachDepthFirstImpl(host, c, fn, user);
80 }
81}
82
83void walkDepthFirstImpl(SceneHost *host, int nodeIndex, const SceneHost::NodeVisitFn &fn) {
84 if (!host || nodeIndex < 0 || !fn) return;
85 auto t = host->tree();
86 if (nodeIndex >= int(t->nodes.size())) return;
87 fn(host, nodeIndex, t->nodes[size_t(nodeIndex)]);
88 for (int c = t->nodes[size_t(nodeIndex)].firstChild; c >= 0;
89 c = t->nodes[size_t(c)].nextSibling) {
90 walkDepthFirstImpl(host, c, fn);
91 }
92}
93
94void walkBreadthFirstImpl(SceneHost *host, int nodeIndex, const SceneHost::NodeVisitFn &fn) {
95 if (!host || nodeIndex < 0 || !fn) return;
96 auto t = host->tree();
97 if (nodeIndex >= int(t->nodes.size())) return;
98 std::vector<int> queue;
99 queue.push_back(nodeIndex);
100 for (size_t i = 0; i < queue.size(); ++i) {
101 const int idx = queue[i];
102 fn(host, idx, t->nodes[size_t(idx)]);
103 for (int c = t->nodes[size_t(idx)].firstChild; c >= 0;
104 c = t->nodes[size_t(c)].nextSibling) {
105 queue.push_back(c);
106 }
107 }
108}
109
110std::vector<std::string> splitPath(const std::string &path) {
111 std::vector<std::string> parts;
112 std::string cur;
113 for (char ch : path) {
114 if (ch == '/') {
115 if (!cur.empty()) {
116 parts.push_back(cur);
117 cur.clear();
118 }
119 } else {
120 cur.push_back(ch);
121 }
122 }
123 if (!cur.empty()) parts.push_back(cur);
124 return parts;
125}
126
127} // namespace
128
130 SceneHost *h = SceneHost::create();
131 if (!h)
133 eve::Diagnostic::error(eve::DiagnosticCode::Failed, "scene host creation returned null", "scene.host"));
134 h->meta()->entity = h;
135 if (name.empty()) {
136 h->meta()->name = "host" + std::to_string(++g_anonHostSeq);
137 } else {
138 h->meta()->name = name;
139 }
141}
142
143void SceneHost::setName(const std::string &name) { meta()->name = name; }
144
145const std::string &SceneHost::getName() { return meta()->name; }
146
147void SceneHost::setTree(NodeDesc root) { applyTree(this, std::move(root)); }
148
150
152 if (node.id.empty() || hasNode(node.id)) return SceneMutationStatus::Rejected;
153 const int parentIndex = parentId.empty() ? -1 : findIndexById(parentId);
154 if (!parentId.empty() && parentIndex < 0) return SceneMutationStatus::Rejected;
155 node.parent = -1;
156 node.firstChild = -1;
157 node.nextSibling = -1;
158 node.localDirty = true;
159 node.subtreeDirty = true;
160 auto value = tree();
161 const int index = static_cast<int>(value->nodes.size());
162 value->nodes.push_back(std::move(node));
163 if (parentIndex >= 0)
164 linkAsLastChild(*value, parentIndex, index);
165 else if (value->root < 0)
166 value->root = index;
167 value->dirty = true;
168 value->transformDirty = true;
169 value->indexValid = false;
170 fireEvent("node_added", value->nodes[size_t(index)].id, parentId);
172}
173
175 const int index = findIndexById(nodeId);
176 auto value = tree();
177 if (index < 0 || value->nodes[size_t(index)].firstChild >= 0) return SceneMutationStatus::Rejected;
178 const int oldParent = value->nodes[size_t(index)].parent;
179 const std::string parentId = oldParent >= 0 ? value->nodes[size_t(oldParent)].id : std::string{};
180 unlinkFromParent(*value, index);
181 value->nodes.erase(value->nodes.begin() + index);
182 for (SceneNode &node : value->nodes) {
183 if (node.parent > index) --node.parent;
184 if (node.firstChild > index) --node.firstChild;
185 if (node.nextSibling > index) --node.nextSibling;
186 }
187 if (value->root == index)
188 value->root = value->nodes.empty() ? -1 : 0;
189 else if (value->root > index)
190 --value->root;
191 value->dirty = true;
192 value->transformDirty = true;
193 value->indexValid = false;
194 fireEvent("node_removed", nodeId, parentId);
196}
197
198SceneMutationStatus SceneHost::renameNode(const std::string &nodeId, const std::string &name) {
199 if (name.empty()) return SceneMutationStatus::Rejected;
200 const int index = findIndexById(nodeId);
201 if (index < 0) return SceneMutationStatus::Rejected;
202 tree()->nodes[size_t(index)].name = name;
203 tree()->dirty = true;
204 fireEvent("node_changed", nodeId);
206}
207
208SceneMutationStatus SceneHost::setLocalTransform(const std::string &nodeId, float x, float y, float z,
209 float yaw, float pitch, float roll, float sx, float sy, float sz) {
210 const int index = findIndexById(nodeId);
211 if (index < 0 || sx == 0.f || sy == 0.f || sz == 0.f) return SceneMutationStatus::Rejected;
212 SceneNode &node = tree()->nodes[size_t(index)];
213 node.x = x;
214 node.y = y;
215 node.z = z;
216 node.yaw = yaw;
217 node.pitch = pitch;
218 node.roll = roll;
219 node.sx = sx;
220 node.sy = sy;
221 node.sz = sz;
223 fireEvent("node_changed", nodeId);
225}
226
228 if (id.empty())
230 eve::DiagnosticCode::InvalidArgument, "scene node id must not be empty", "scene.host"));
231 auto t = tree();
232 for (auto &n : t->nodes) {
234 }
236 eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "scene node id was not found: " + id, "scene.host"));
237}
238
240 if (key.empty())
242 eve::DiagnosticCode::InvalidArgument, "scene node key must not be empty", "scene.host"));
243 auto t = tree();
244 for (auto &n : t->nodes) {
246 }
248 eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "scene node key was not found: " + key, "scene.host"));
249}
250
252 if (name.empty())
254 eve::DiagnosticCode::InvalidArgument, "scene node name must not be empty", "scene.host"));
255 const int idx = findIndexByName(name);
256 if (idx < 0)
258 eve::DiagnosticCode::NotFound, "scene node name was not found: " + name, "scene.host"));
260}
261
263 if (path.empty())
265 eve::DiagnosticCode::InvalidArgument, "scene node path must not be empty", "scene.host"));
266 const int idx = findIndexByPath(path);
267 if (idx < 0)
269 eve::DiagnosticCode::NotFound, "scene node path was not found: " + path, "scene.host"));
271}
272
273int SceneHost::findIndexById(const std::string &id) {
274 if (id.empty()) return -1;
275 auto t = tree();
276 if (!t->indexValid) {
277 t->idIndex.clear();
278 for (int i = 0; i < int(t->nodes.size()); ++i) {
279 const auto &nid = t->nodes[size_t(i)].id;
280 if (!nid.empty()) t->idIndex[nid] = i;
281 }
282 t->indexValid = true;
283 }
284 auto it = t->idIndex.find(id);
285 return it != t->idIndex.end() ? it->second : -1;
286}
287
288int SceneHost::findIndexByKey(const std::string &key) {
289 if (key.empty()) return -1;
290 auto t = tree();
291 for (int i = 0; i < int(t->nodes.size()); ++i) {
292 if (t->nodes[size_t(i)].key == key) return i;
293 }
294 return -1;
295}
296
297int SceneHost::findIndexByName(const std::string &name) {
298 if (name.empty()) return -1;
299 int found = -1;
301 if (found >= 0) return;
302 if (n.name == name) found = index;
303 });
304 return found;
305}
306
307int SceneHost::findIndexByPath(const std::string &path) {
308 auto parts = splitPath(path);
309 if (parts.empty()) return -1;
310 auto t = tree();
311 if (t->root < 0) return -1;
312
313 int idx = t->root;
314 size_t start = 0;
315 if (t->nodes[size_t(idx)].id == parts[0] || t->nodes[size_t(idx)].name == parts[0]) {
316 start = 1;
317 }
318 for (size_t p = start; p < parts.size(); ++p) {
319 const std::string &want = parts[p];
320 int match = -1;
321 for (int child = t->nodes[size_t(idx)].firstChild; child >= 0;
322 child = t->nodes[size_t(child)].nextSibling) {
323 const SceneNode &n = t->nodes[size_t(child)];
324 if (n.id == want || n.name == want) {
325 match = child;
326 break;
327 }
328 }
329 if (match < 0) return -1;
330 idx = match;
331 }
332 return idx;
333}
334
335bool SceneHost::hasNode(const std::string &id) { return findIndexById(id) >= 0; }
336
337int SceneHost::getNodeCount() { return int(tree()->nodes.size()); }
338
339int SceneHost::getRootIndex() { return tree()->root; }
340
342 const int r = tree()->root;
343 if (r < 0) return nullptr;
344 return &tree()->nodes[size_t(r)];
345}
346
348 auto t = tree();
349 if (index < 0 || index >= int(t->nodes.size())) return nullptr;
350 return &t->nodes[size_t(index)];
351}
352
354 auto t = tree();
355 if (nodeIndex < 0 || nodeIndex >= int(t->nodes.size())) return -1;
356 return t->nodes[size_t(nodeIndex)].parent;
357}
358
360
361SceneNode *SceneHost::getParentById(const std::string &id) { return getParent(findIndexById(id)); }
362
364 auto t = tree();
365 if (nodeIndex < 0 || nodeIndex >= int(t->nodes.size())) return 0;
366 int count = 0;
367 for (int c = t->nodes[size_t(nodeIndex)].firstChild; c >= 0;
368 c = t->nodes[size_t(c)].nextSibling) {
369 ++count;
370 }
371 return count;
372}
373
374int SceneHost::getChildCountById(const std::string &id) { return getChildCount(findIndexById(id)); }
375
376int SceneHost::getChildIndexAt(int parentIndex, int childOrdinal) {
377 if (childOrdinal < 0) return -1;
378 auto t = tree();
379 if (parentIndex < 0 || parentIndex >= int(t->nodes.size())) return -1;
380 int i = 0;
381 for (int c = t->nodes[size_t(parentIndex)].firstChild; c >= 0;
382 c = t->nodes[size_t(c)].nextSibling) {
383 if (i == childOrdinal) return c;
384 ++i;
385 }
386 return -1;
387}
388
389SceneNode *SceneHost::getChildAt(int parentIndex, int childOrdinal) {
390 return getNode(getChildIndexAt(parentIndex, childOrdinal));
391}
392
393SceneNode *SceneHost::getChildAtById(const std::string &parentId, int childOrdinal) {
394 return getChildAt(findIndexById(parentId), childOrdinal);
395}
396
398 std::vector<int> out;
399 auto t = tree();
400 if (nodeIndex < 0 || nodeIndex >= int(t->nodes.size())) return out;
401 for (int c = t->nodes[size_t(nodeIndex)].firstChild; c >= 0;
402 c = t->nodes[size_t(c)].nextSibling) {
403 out.push_back(c);
404 }
405 return out;
406}
407
408std::vector<SceneNode *> SceneHost::getChildren(int nodeIndex) {
409 std::vector<SceneNode *> out;
410 for (int c : getChildIndices(nodeIndex)) out.push_back(&tree()->nodes[size_t(c)]);
411 return out;
412}
413
414std::vector<std::string> SceneHost::getChildIds(const std::string &parentId) {
415 std::vector<std::string> out;
417 if (n) out.push_back(n->id);
418 }
419 return out;
420}
421
423 auto t = tree();
424 if (nodeIndex < 0 || nodeIndex >= int(t->nodes.size())) return {};
425 std::vector<std::string> parts;
426 for (int i = nodeIndex; i >= 0; i = t->nodes[size_t(i)].parent) {
427 parts.push_back(t->nodes[size_t(i)].id);
428 }
429 std::string path;
430 for (int i = int(parts.size()) - 1; i >= 0; --i) {
431 if (!path.empty()) path += '/';
432 path += parts[size_t(i)];
433 }
434 return path;
435}
436
437std::string SceneHost::getPathById(const std::string &id) { return getPath(findIndexById(id)); }
438
439bool SceneHost::isAncestorOf(int ancestorIndex, int nodeIndex) {
440 auto t = tree();
441 if (ancestorIndex < 0 || nodeIndex < 0) return false;
442 if (ancestorIndex >= int(t->nodes.size()) || nodeIndex >= int(t->nodes.size())) return false;
443 return isAncestor(*t, ancestorIndex, nodeIndex);
444}
445
446bool SceneHost::isAncestorOfById(const std::string &ancestorId, const std::string &nodeId) {
447 return isAncestorOf(findIndexById(ancestorId), findIndexById(nodeId));
448}
449
450bool SceneHost::isDescendantOf(int nodeIndex, int ancestorIndex) {
451 return isAncestorOf(ancestorIndex, nodeIndex);
452}
453
454bool SceneHost::isDescendantOfById(const std::string &nodeId, const std::string &ancestorId) {
455 return isAncestorOfById(ancestorId, nodeId);
456}
457
458bool SceneHost::setParent(int childIndex, int parentIndex) {
459 auto t = tree();
460 if (childIndex < 0 || childIndex >= int(t->nodes.size())) return false;
461 if (parentIndex < -1 || parentIndex >= int(t->nodes.size())) return false;
462 if (childIndex == parentIndex) return false;
463 if (parentIndex >= 0 && isAncestor(*t, childIndex, parentIndex)) return false;
464
465 unlinkFromParent(*t, childIndex);
466 if (parentIndex < 0) {
467 t->nodes[size_t(childIndex)].parent = -1;
468 t->nodes[size_t(childIndex)].nextSibling = -1;
469 markSubtreeDirty(childIndex);
470 fireEvent("node_moved", t->nodes[size_t(childIndex)].id, "");
471 return true;
472 }
473 linkAsLastChild(*t, parentIndex, childIndex);
474 markSubtreeDirty(childIndex);
475 markSubtreeDirty(parentIndex);
476 fireEvent("node_moved", t->nodes[size_t(childIndex)].id,
477 t->nodes[size_t(parentIndex)].id);
478 return true;
479}
480
481bool SceneHost::addChild(int parentIndex, int childIndex) { return setParent(childIndex, parentIndex); }
482
483bool SceneHost::removeChild(int parentIndex, int childIndex) {
484 auto t = tree();
485 if (childIndex < 0 || childIndex >= int(t->nodes.size())) return false;
486 if (t->nodes[size_t(childIndex)].parent != parentIndex) return false;
487 unlinkFromParent(*t, childIndex);
488 markSubtreeDirty(childIndex);
489 markSubtreeDirty(parentIndex);
490 fireEvent("node_moved", t->nodes[size_t(childIndex)].id, "");
491 return true;
492}
493
495 auto t = tree();
496 if (nodeIndex < 0 || nodeIndex >= int(t->nodes.size())) return;
497 t->nodes[size_t(nodeIndex)].subtreeDirty = true;
498 t->nodes[size_t(nodeIndex)].localDirty = true;
499}
500
501void SceneHost::fireEvent(const std::string &action, const std::string &nodeId,
502 const std::string &parentId) {
503 if (eventHandler_) eventHandler_(this, action, nodeId, parentId);
504}
505
506void SceneHost::forEachDepthFirst(int nodeIndex, void (*fn)(SceneHost *, int, void *), void *user) {
507 forEachDepthFirstImpl(this, nodeIndex, fn, user);
508}
509
511
513 walkDepthFirstImpl(this, nodeIndex, fn);
514}
515
519
521 walkBreadthFirstImpl(this, nodeIndex, fn);
522}
523
524void SceneHost::walkChildren(int parentIndex, NodeVisitFn fn) {
525 if (!fn) return;
526 for (int c : getChildIndices(parentIndex)) {
527 fn(this, c, tree()->nodes[size_t(c)]);
528 }
529}
530
532 if (!fn) return;
533 auto t = tree();
534 if (nodeIndex < 0 || nodeIndex >= int(t->nodes.size())) return;
535 for (int p = t->nodes[size_t(nodeIndex)].parent; p >= 0; p = t->nodes[size_t(p)].parent) {
536 fn(this, p, t->nodes[size_t(p)]);
537 }
538}
539
540SceneNode *SceneHost::findIf(NodePredFn pred) { return findIfFrom(tree()->root, std::move(pred)); }
541
543 if (!pred) return nullptr;
544 SceneNode *found = nullptr;
546 if (found) return;
547 if (pred(host, index, node)) found = &node;
548 });
549 return found;
550}
551
552std::vector<SceneNode *> SceneHost::filter(NodePredFn pred) {
553 return filterFrom(tree()->root, std::move(pred));
554}
555
556std::vector<SceneNode *> SceneHost::filterFrom(int nodeIndex, NodePredFn pred) {
557 std::vector<SceneNode *> out;
558 if (!pred) return out;
560 if (pred(host, index, node)) out.push_back(&node);
561 });
562 return out;
563}
564
565std::vector<int> SceneHost::filterIndices(NodePredFn pred) {
566 return filterIndicesFrom(tree()->root, std::move(pred));
567}
568
570 std::vector<int> out;
571 if (!pred) return out;
573 if (pred(host, index, node)) out.push_back(index);
574 });
575 return out;
576}
577
578std::vector<SceneNode *> SceneHost::findAllByName(const std::string &name) {
579 return filter([&](SceneHost *, int, const SceneNode &n) { return n.name == name; });
580}
581
582std::vector<SceneNode *> SceneHost::findAllByKey(const std::string &key) {
583 return filter([&](SceneHost *, int, const SceneNode &n) { return n.key == key; });
584}
585
586std::vector<SceneNode *> SceneHost::findAllVisible(bool visible) {
587 return filter([&](SceneHost *, int, const SceneNode &n) { return n.visible == visible; });
588}
589
590std::vector<SceneNode *> SceneHost::findAllBySpace(const std::string &space) {
591 return filter([&](SceneHost *, int, const SceneNode &n) { return n.space == space; });
592}
593
594std::vector<SceneNode *> SceneHost::findAllLinked() {
595 return filter([&](SceneHost *, int, const SceneNode &n) {
596 return !n.links.empty();
597 });
598}
599
600std::vector<std::string> SceneHost::collectIds() { return collectIdsFrom(tree()->root); }
601
602std::vector<std::string> SceneHost::collectIdsFrom(int nodeIndex) {
603 std::vector<std::string> out;
604 walkDepthFirstFrom(nodeIndex, [&](SceneHost *, int, SceneNode &n) { out.push_back(n.id); });
605 return out;
606}
607
608std::vector<std::string> SceneHost::collectIdsWhere(NodePredFn pred) {
609 std::vector<std::string> out;
610 for (SceneNode *n : filter(std::move(pred))) {
611 if (n) out.push_back(n->id);
612 }
613 return out;
614}
615
616std::vector<std::string> SceneHost::collectIdsByName(const std::string &name) {
617 return collectIdsWhere([&](SceneHost *, int, const SceneNode &n) { return n.name == name; });
618}
619
620std::vector<std::string> SceneHost::collectIdsVisible(bool visible) {
621 return collectIdsWhere([&](SceneHost *, int, const SceneNode &n) { return n.visible == visible; });
622}
623
624bool SceneHost::link(const std::string &nodeId, int kind, void *target, int syncMode) {
625 if (!linkOps(kind)) return false; // kind's module is not in this build
626 SceneNode *n = borrowSceneResult(findById(nodeId));
627 return n ? setLink(*n, kind, target, syncMode) : false;
628}
629
630// The typed helpers resolve their kind by name, so they return false rather
631// than crashing when the module that registers it was trimmed out.
633 return link(nodeId, findLinkKind("renderable2d"), r, 0);
634}
635
637 return link(nodeId, findLinkKind("renderable3d"), r, 0);
638}
639
640bool SceneHost::linkPhysics2D(const std::string &nodeId, physics::Body *b, int syncMode) {
641 return link(nodeId, findLinkKind("physics2d"), b, syncMode);
642}
643
644bool SceneHost::linkPhysics3D(const std::string &nodeId, physics::Body3D *b, int syncMode) {
645 return link(nodeId, findLinkKind("physics3d"), b, syncMode);
646}
647
649 return link(nodeId, findLinkKind("camera3d"), c, 0);
650}
651
652bool SceneHost::linkAudio3D(const std::string &nodeId, audio::Source *s) {
653 return link(nodeId, findLinkKind("audio3d"), s, 0);
654}
655
656bool SceneHost::unlink(const std::string &nodeId, int kind) {
657 SceneNode *n = borrowSceneResult(findById(nodeId));
658 if (!n) return false;
659 for (auto it = n->links.begin(); it != n->links.end(); ++it) {
660 if (it->kind == kind) {
661 n->links.erase(it);
662 return true;
663 }
664 }
665 return false;
666}
667
668bool SceneHost::unlink(const std::string &nodeId) {
669 SceneNode *n = borrowSceneResult(findById(nodeId));
670 if (!n) return false;
671 n->links.clear();
672 return true;
673}
674
676 if (!node) return nullptr;
677 for (auto &l : node->links) {
678 if (l.kind == kind) return &l;
679 }
680 return nullptr;
681}
682
684 if (!node) return nullptr;
685 for (const auto &l : node->links) {
686 if (l.kind == kind) return &l;
687 }
688 return nullptr;
689}
690
691int SceneHost::linkCount(const std::string &nodeId) {
692 SceneNode *n = borrowSceneResult(findById(nodeId));
693 return n ? int(n->links.size()) : 0;
694}
695
696bool SceneHost::setParentById(const std::string &childId, const std::string &parentId) {
697 const int childIndex = findIndexById(childId);
698 if (childIndex < 0) return false;
699 const int parentIndex = parentId.empty() ? -1 : findIndexById(parentId);
700 if (parentIndex < 0 && !parentId.empty()) return false;
701 return setParent(childIndex, parentIndex);
702}
703
704bool SceneHost::removeChildById(const std::string &parentId, const std::string &childId) {
705 const int parentIndex = findIndexById(parentId);
706 const int childIndex = findIndexById(childId);
707 if (parentIndex < 0 || childIndex < 0) return false;
708 return removeChild(parentIndex, childIndex);
709}
710
711bool SceneHost::addTag(SceneNode *node, const std::string &tag) {
712 if (!node || tag.empty()) return false;
713 for (const auto &t : node->tags) {
714 if (t == tag) return false;
715 }
716 node->tags.push_back(tag);
717 return true;
718}
719
720bool SceneHost::removeTag(SceneNode *node, const std::string &tag) {
721 if (!node) return false;
722 for (auto it = node->tags.begin(); it != node->tags.end(); ++it) {
723 if (*it == tag) {
724 node->tags.erase(it);
725 return true;
726 }
727 }
728 return false;
729}
730
731bool SceneHost::hasTag(const SceneNode *node, const std::string &tag) const {
732 if (!node) return false;
733 for (const auto &t : node->tags) {
734 if (t == tag) return true;
735 }
736 return false;
737}
738
739std::vector<SceneNode *> SceneHost::findAllByTag(const std::string &tag) {
740 return filter([&](SceneHost *, int, const SceneNode &n) {
741 for (const auto &t : n.tags) {
742 if (t == tag) return true;
743 }
744 return false;
745 });
746}
747
748} // namespace eve::scene
LogicalId target
double value
Duration start
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
std::vector< eve::artifact::PartView > parts
const std::string & s
glm::vec4 p[6]
std::string nodeId
std::uint64_t parentId
std::uint32_t key
glm::vec3 n
Definition Grass.cpp:63
double r
std::int32_t c
int h
TokenKind kind
std::int32_t parent
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
std::vector< BvhNode > nodes
const std::string * tag
int idx
std::string action
Definition PlayHost.cpp:117
std::string path
Definition PlayHost.cpp:110
float t
const RoadNode * node
bool found
std::string filter
std::uint32_t count
bool visible
UIHostHandle host
uint32_t index
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
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 Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
A playable audio source (static buffer or streaming decoder). Not thread-safe for playback control ex...
Definition Source.h:30
EVENGINE_API_BACKENDS public API.
Default renderable entity for declarative 2D sprites / solid quads.
EVENGINE_API_BACKENDS public API.
3D rigid body (Box3D) in meter-space coordinates (+Y up by convention). Owned by a World3D; create pr...
Definition Body3D.h:24
2D rigid body (Box2D) in pixel-space coordinates. Owned by a World; create shapes with newRectangleFi...
Definition Body.h:21
ECS mount point for one scene graph (full scene or nested subtree root). Isomorphic to eve::ui::UIHos...
Definition SceneHost.h:89
bool isDescendantOf(int nodeIndex, int ancestorIndex)
eve::Result< SceneNode * > findByName(const std::string &name)
Finds the first node name match with an explicit status.
bool isAncestorOfById(const std::string &ancestorId, const std::string &nodeId)
std::vector< SceneNode * > getChildren(int nodeIndex)
void walkBreadthFirstFrom(int nodeIndex, NodeVisitFn fn)
SceneMutationStatus setLocalTransform(const std::string &nodeId, float x, float y, float z, float yaw, float pitch, float roll, float sx, float sy, float sz)
Replace a node's local TRS and mark its subtree dirty.
bool isDescendantOfById(const std::string &nodeId, const std::string &ancestorId)
int findIndexByName(const std::string &name)
int findIndexById(const std::string &id)
void walkDepthFirstFrom(int nodeIndex, NodeVisitFn fn)
bool setParent(int childIndex, int parentIndex)
Imperative parent link. Returns false on invalid indices or cycle. Prefer declarative NodeDesc + reco...
void setTree(NodeDesc root)
Full replace.
bool removeChild(int parentIndex, int childIndex)
bool link(const std::string &nodeId, int kind, void *target, int syncMode=0)
Attach an external object to a node id. Survives reconcile/rebuild by id. After TransformSystem the l...
std::vector< int > filterIndices(NodePredFn pred)
bool removeChildById(const std::string &parentId, const std::string &childId)
int getChildIndexAt(int parentIndex, int childOrdinal)
Child ordinal 0..count-1; -1 if out of range.
bool setTreeReconcile(NodeDesc root)
Key-aware patch when structure matches; else full replace.
std::vector< SceneNode * > findAllBySpace(const std::string &space)
int getChildCountById(const std::string &id)
bool linkRenderable3D(const std::string &nodeId, graphics::Renderable3D *r)
bool linkPhysics3D(const std::string &nodeId, physics::Body3D *b, int syncMode=0)
bool isAncestorOf(int ancestorIndex, int nodeIndex)
std::vector< int > filterIndicesFrom(int nodeIndex, NodePredFn pred)
SceneNode * getChildAt(int parentIndex, int childOrdinal)
std::vector< std::string > collectIdsVisible(bool visible=true)
std::vector< SceneNode * > filterFrom(int nodeIndex, NodePredFn pred)
std::vector< std::string > getChildIds(const std::string &parentId)
std::function< bool(SceneHost *host, int index, const SceneNode &node)> NodePredFn
Definition SceneHost.h:254
eve::Result< SceneNode * > findById(const std::string &id)
Finds a node by stable id with an explicit status.
std::vector< std::string > collectIdsFrom(int nodeIndex)
static eve::Result< SceneHost * > createHost(const std::string &name="")
Creates an ECS-owned scene host with an explicit Result contract.
std::vector< std::string > collectIdsWhere(NodePredFn pred)
std::vector< std::string > collectIdsByName(const std::string &name)
void forEachDepthFirst(int nodeIndex, void(*fn)(SceneHost *, int, void *), void *user)
Depth-first visit of arena indices under nodeIndex (inclusive). C callback.
SceneNode * findIf(NodePredFn pred)
First DFS match; nullptr if none.
std::vector< SceneNode * > findAllLinked()
void setName(const std::string &name)
eve::Result< SceneNode * > findByPath(const std::string &path)
Finds a node path with an explicit status.
SceneMutationStatus removeLeaf(const std::string &nodeId)
Remove a leaf node while preserving every other node and external link.
SceneMutationStatus renameNode(const std::string &nodeId, const std::string &name)
Rename a retained node and emit a node_changed event.
bool linkPhysics2D(const std::string &nodeId, physics::Body *b, int syncMode=0)
SceneNode * getParentById(const std::string &id)
std::string getPathById(const std::string &id)
SceneMutationStatus appendNode(SceneNode node, const std::string &parentId={})
Append one retained node without rebuilding existing nodes or links.
int getParentIndex(int nodeIndex)
std::vector< int > getChildIndices(int nodeIndex)
void markSubtreeDirty(int nodeIndex)
Mark a node's subtree for world recompute (recompute it + descendants).
bool addChild(int parentIndex, int childIndex)
int findIndexByKey(const std::string &key)
bool addTag(SceneNode *node, const std::string &tag)
std::vector< std::string > collectIds()
std::vector< SceneNode * > findAllVisible(bool visible=true)
void walkAncestors(int nodeIndex, NodeVisitFn fn)
Parent chain upward, excluding self.
std::vector< SceneNode * > filter(NodePredFn pred)
int findIndexByPath(const std::string &path)
std::vector< SceneNode * > findAllByTag(const std::string &tag)
SceneNode * getParent(int nodeIndex)
int linkCount(const std::string &nodeId)
bool hasTag(const SceneNode *node, const std::string &tag) const
SceneNode * findIfFrom(int nodeIndex, NodePredFn pred)
bool linkAudio3D(const std::string &nodeId, audio::Source *s)
bool removeTag(SceneNode *node, const std::string &tag)
const std::string & getName()
bool unlink(const std::string &nodeId, int kind)
Remove all links of a kind; returns true if a link was removed.
std::string getPath(int nodeIndex)
Id path "a/b/c" from root to node; empty if invalid.
std::vector< SceneNode * > findAllByKey(const std::string &key)
void walkChildren(int parentIndex, NodeVisitFn fn)
Direct children only.
bool linkCamera3D(const std::string &nodeId, graphics::Camera3D *c)
std::function< void(SceneHost *host, int index, SceneNode &node)> NodeVisitFn
Definition SceneHost.h:253
void walkDepthFirst(NodeVisitFn fn)
DFS from root (or from nodeIndex), inclusive.
void walkBreadthFirst(NodeVisitFn fn)
BFS from root (or from nodeIndex), inclusive.
bool hasNode(const std::string &id)
eve::Result< SceneNode * > findByKey(const std::string &key)
Finds a node by reconcile key with an explicit status.
bool linkRenderable2D(const std::string &nodeId, graphics::Renderable2D *r)
std::vector< SceneNode * > findAllByName(const std::string &name)
SceneNode * getRoot()
SceneLink * findLink(SceneNode *node, int kind)
int getChildCount(int nodeIndex)
SceneNode * getChildAtById(const std::string &parentId, int childOrdinal)
SceneNode * getNode(int index)
void fireEvent(const std::string &action, const std::string &nodeId, const std::string &parentId={})
Fire "node_added" / "node_removed" / "node_moved" / "node_changed".
bool setParentById(const std::string &childId, const std::string &parentId)
Reparent by id; empty parentId detaches (parent = -1). Cycle-safe.
bool applyTreeReconcile(SceneHost *host, NodeDesc root)
Key-aware patch when structure matches; else full replace. Returns true if full rebuild.
Definition NodeDesc.cpp:323
SceneMutationStatus
Outcome of an atomic scene-tree mutation.
Definition SceneHost.h:41
int findLinkKind(const char *kind)
Finds link kind.
Definition SceneLink.cpp:39
const LinkOps * linkOps(int kindId)
Link ops.
Definition SceneLink.cpp:47
void applyTree(SceneHost *host, NodeDesc root)
Applies tree.
Definition NodeDesc.cpp:243
WidgetDesc child(std::string id, std::vector< WidgetDesc > children, float width, float height)
Scrollable child region with an explicit size.
Definition Widget.cpp:635
SettlementPipeline::Stage fn
Declarative scene-node description (build once / on dirty → flatten into SceneHost::Tree)....
Definition NodeDesc.h:19
Retained scene node (arena). Conceptual GameObject; isomorphic to eve::ui::UINode.
Definition SceneHost.h:46