载入中...
搜索中...
未找到
NodeDesc.cpp
浏览该文件的文档.
1#include "scene/NodeDesc.h"
2
3#include "scene/SceneHost.h"
4
5#include <functional>
6#include <stdexcept>
7#include <unordered_map>
8#include <unordered_set>
9
10namespace eve::scene {
11namespace {
12
13int appendDetachedNode(SceneHost::Tree& tree, NodeDesc&& desc, int parentIndex) {
14 const int index = int(tree.nodes.size());
15 SceneNode node;
16 node.id = std::move(desc.id);
17 node.persistentId = desc.persistentId;
18 node.key = desc.key.empty() ? node.id : std::move(desc.key);
19 node.name = desc.name.empty() ? node.id : std::move(desc.name);
20 node.space = std::move(desc.space);
21 if (node.space.empty()) node.space = "3d";
22 node.visible = desc.visible;
23 node.tags = std::move(desc.tags);
24 node.layer = desc.layer;
25 node.bminX = desc.bminX;
26 node.bminY = desc.bminY;
27 node.bminZ = desc.bminZ;
28 node.bmaxX = desc.bmaxX;
29 node.bmaxY = desc.bmaxY;
30 node.bmaxZ = desc.bmaxZ;
31 node.hasBounds = desc.hasBounds;
32 node.x = desc.x;
33 node.y = desc.y;
34 node.z = desc.z;
35 node.yaw = desc.yaw;
36 node.pitch = desc.pitch;
37 node.roll = desc.roll;
38 node.sx = desc.sx;
39 node.sy = desc.sy;
40 node.sz = desc.sz;
41 node.localDirty = true;
42 node.world = glm::mat4(1.f);
43 node.firstChild = -1;
44 node.nextSibling = -1;
45 node.parent = parentIndex;
46
47 tree.nodes.push_back(std::move(node));
48
49 int prevChild = -1;
50 int firstChild = -1;
51 for (auto &child : desc.children) {
52 int childIndex = appendDetachedNode(tree, std::move(child), index);
53 if (firstChild < 0) firstChild = childIndex;
54 if (prevChild >= 0) tree.nodes[size_t(prevChild)].nextSibling = childIndex;
55 prevChild = childIndex;
56 }
57 tree.nodes[size_t(index)].firstChild = firstChild;
58 return index;
59}
60
61bool structureMatches(const SceneHost::Tree &tree, int nodeIndex, const NodeDesc &desc) {
62 if (nodeIndex < 0 || nodeIndex >= int(tree.nodes.size())) return false;
63 const SceneNode &n = tree.nodes[size_t(nodeIndex)];
64 const std::string &dk = desc.reconcileKey();
65 if (!dk.empty() && !n.key.empty() && n.key != dk) return false;
66
67 std::vector<std::string> oldKeys;
68 for (int c = n.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
69 oldKeys.push_back(tree.nodes[size_t(c)].key);
70 }
71 if (oldKeys.size() != desc.children.size()) return false;
72 for (size_t i = 0; i < desc.children.size(); ++i) {
73 const std::string &ck = desc.children[i].reconcileKey();
74 if (ck.empty() || oldKeys[i].empty()) {
75 if (!(ck.empty() && oldKeys[i].empty())) return false;
76 } else if (ck != oldKeys[i]) {
77 return false;
78 }
79 }
80
81 int child = n.firstChild;
82 for (const auto &ch : desc.children) {
83 if (!structureMatches(tree, child, ch)) return false;
84 child = tree.nodes[size_t(child)].nextSibling;
85 }
86 return true;
87}
88
93bool structureMatchesSet(const SceneHost::Tree &tree, int nodeIndex,
94 const NodeDesc &desc) {
95 if (nodeIndex < 0 || nodeIndex >= int(tree.nodes.size())) return false;
96 const SceneNode &n = tree.nodes[size_t(nodeIndex)];
97 const std::string &dk = desc.reconcileKey();
98 if (!dk.empty() && !n.key.empty() && n.key != dk) return false;
99
100 std::vector<std::string> oldKeys;
101 for (int c = n.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
102 oldKeys.push_back(tree.nodes[size_t(c)].key);
103 }
104 if (oldKeys.size() != desc.children.size()) return false;
105
106 std::unordered_multiset<std::string> want;
107 for (const auto &ch : desc.children) want.insert(ch.reconcileKey());
108 for (const auto &k : oldKeys) {
109 auto it = want.find(k);
110 if (it == want.end()) return false;
111 want.erase(it);
112 }
113
114 // Pair every old child with a same-key desc child and recurse.
115 std::vector<bool> used(desc.children.size(), false);
116 for (int c = n.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
117 bool found = false;
118 const std::string &ck = tree.nodes[size_t(c)].key;
119 for (size_t i = 0; i < desc.children.size(); ++i) {
120 if (used[i]) continue;
121 const std::string &dk2 = desc.children[i].reconcileKey();
122 if (ck == dk2 || (ck.empty() && dk2.empty())) {
123 if (structureMatchesSet(tree, c, desc.children[i])) {
124 used[i] = true;
125 found = true;
126 break;
127 }
128 }
129 }
130 if (!found) return false;
131 }
132 return true;
133}
134
136bool reorderChildrenRecursive(SceneHost::Tree &tree, int nodeIndex,
137 const NodeDesc &desc) {
138 SceneNode &n = tree.nodes[size_t(nodeIndex)];
139 std::unordered_map<std::string, std::vector<int>> byKey;
140 for (int c = n.firstChild; c >= 0; c = tree.nodes[size_t(c)].nextSibling) {
141 byKey[tree.nodes[size_t(c)].key].push_back(c);
142 }
143 std::vector<int> order;
144 order.reserve(desc.children.size());
145 for (const auto &ch : desc.children) {
146 auto it = byKey.find(ch.reconcileKey());
147 if (it == byKey.end() || it->second.empty()) return false;
148 order.push_back(it->second.back());
149 it->second.pop_back();
150 }
151 if (order.empty()) {
152 n.firstChild = -1;
153 } else {
154 n.firstChild = order[0];
155 for (size_t i = 0; i < order.size(); ++i) {
156 tree.nodes[size_t(order[i])].nextSibling =
157 (i + 1 < order.size()) ? order[i + 1] : -1;
158 }
159 }
160 for (size_t i = 0; i < order.size(); ++i) {
161 if (!reorderChildrenRecursive(tree, order[i], desc.children[i])) return false;
162 }
163 return true;
164}
165
166void patchProps(SceneHost *host, int nodeIndex, NodeDesc &&desc) {
167 SceneHost::Tree &tree = *host->tree();
168 SceneNode &n = tree.nodes[size_t(nodeIndex)];
169 n.visible = desc.visible;
170 n.tags = desc.tags;
171 n.layer = desc.layer;
172 n.bminX = desc.bminX;
173 n.bminY = desc.bminY;
174 n.bminZ = desc.bminZ;
175 n.bmaxX = desc.bmaxX;
176 n.bmaxY = desc.bmaxY;
177 n.bmaxZ = desc.bmaxZ;
178 n.hasBounds = desc.hasBounds;
179 n.x = desc.x;
180 n.y = desc.y;
181 n.z = desc.z;
182 n.yaw = desc.yaw;
183 n.pitch = desc.pitch;
184 n.roll = desc.roll;
185 n.sx = desc.sx;
186 n.sy = desc.sy;
187 n.sz = desc.sz;
188 if (!desc.persistentId.isNil()) n.persistentId = desc.persistentId;
189 if (!desc.space.empty()) n.space = desc.space;
190 if (!desc.id.empty()) n.id = desc.id;
191 if (!desc.name.empty()) n.name = desc.name;
192 host->markSubtreeDirty(nodeIndex);
193 host->fireEvent("node_changed", n.id,
194 n.parent >= 0 ? tree.nodes[size_t(n.parent)].id : "");
195
196 int child = n.firstChild;
197 for (auto &ch : desc.children) {
198 patchProps(host, child, std::move(ch));
199 child = tree.nodes[size_t(child)].nextSibling;
200 }
201}
202
203} // namespace
204
206 std::unordered_set<std::string> seen;
207 std::function<void(const NodeDesc &)> walk = [&](const NodeDesc &d) {
208 if (!d.id.empty() && !seen.insert(d.id).second) {
209 throw std::runtime_error("scene: duplicate node id '" + d.id + "'");
210 }
211 for (const auto &c : d.children) walk(c);
212 };
213 walk(root);
214}
215
216NodeDesc node(std::string id, std::vector<NodeDesc> children, std::string name) {
217 NodeDesc d;
218 d.id = std::move(id);
219 d.key = d.id;
220 d.name = name.empty() ? d.id : std::move(name);
221 d.children = std::move(children);
222 return d;
223}
224
225NodeDesc group(std::vector<NodeDesc> children, std::string id) {
226 NodeDesc d;
227 d.id = std::move(id);
228 d.key = d.id;
229 d.name = d.id.empty() ? "group" : d.id;
230 d.children = std::move(children);
231 return d;
232}
233
234NodeDesc when(bool cond, NodeDesc child) {
235 if (!cond) return group({}, "__when_empty");
236 return child;
237}
238
239NodeDesc whenElse(bool cond, NodeDesc ifTrue, NodeDesc ifFalse) {
240 return cond ? std::move(ifTrue) : std::move(ifFalse);
241}
242
244 if (!host) return;
246 auto t = host->tree();
247
248 std::unordered_set<std::string> oldIds;
249 for (const auto &n : t->nodes) {
250 if (!n.id.empty()) oldIds.insert(n.id);
251 }
252
253 std::unordered_map<std::string, std::vector<SceneLink>> saved;
254 for (const auto &n : t->nodes) {
255 if (!n.links.empty() && !n.id.empty()) saved[n.id] = n.links;
256 }
257
258 // Preserve lazy SceneObject bindings by node id (script entities stay alive
259 // across full rebuilds; orphaned SceneObjects are torn down by Scene::prune).
260 std::unordered_map<std::string, uint32_t> savedObjects;
261 for (const auto &n : t->nodes) {
262 if (n.objectId != 0 && !n.id.empty()) savedObjects[n.id] = n.objectId;
263 }
264
265 t->nodes.clear();
266 t->root = -1;
267 t->root = appendDetachedNode(*t, std::move(root), -1);
268 for (auto &n : t->nodes) {
269 auto it = saved.find(n.id);
270 if (it != saved.end()) {
271 n.links = it->second;
272 }
273 auto oit = savedObjects.find(n.id);
274 if (oit != savedObjects.end()) n.objectId = oit->second;
275 }
276 host->invalidateIndex();
277 t->dirty = true;
278 t->transformDirty = true;
279
280 // Node lifecycle events: ids that disappeared were removed; new ids added.
281 std::unordered_set<std::string> newIds;
282 for (const auto &n : t->nodes) {
283 if (!n.id.empty()) newIds.insert(n.id);
284 }
285 for (const auto &id : oldIds) {
286 if (!newIds.count(id)) host->fireEvent("node_removed", id);
287 }
288 for (const auto &n : t->nodes) {
289 if (!oldIds.count(n.id)) {
290 host->fireEvent("node_added", n.id,
291 n.parent >= 0 ? t->nodes[size_t(n.parent)].id : "");
292 }
293 }
294}
295
297 try {
299 Tree target;
300 target.root = appendDetachedNode(target, std::move(root), -1);
301 if (previous) {
302 std::unordered_map<std::string, const SceneNode*> preserved;
303 preserved.reserve(previous->nodes.size());
304 for (const auto& node : previous->nodes)
305 if (!node.id.empty()) preserved.emplace(node.id, &node);
306 for (auto& node : target.nodes) {
307 const auto found = preserved.find(node.id);
308 if (found == preserved.end()) continue;
309 node.links = found->second->links;
310 node.objectId = found->second->objectId;
311 }
312 }
313 target.dirty = true;
314 target.transformDirty = true;
315 target.indexValid = false;
316 return eve::Result<Tree>::success(std::move(target));
317 } catch (const std::exception& exception) {
319 eve::Diagnostic::error(eve::DiagnosticCode::Conflict, exception.what(), "root"));
320 }
321}
322
324 if (!host) return true;
325 auto t = host->tree();
326 if (t->root < 0 || t->nodes.empty() ||
327 !structureMatchesSet(*t, t->root, root)) {
328 applyTree(host, std::move(root));
329 return true;
330 }
331 // Same key set, maybe different order: relink siblings first so patching
332 // follows the new order without rebuilding the arena (links/objects keep
333 // their identity).
334 if (!reorderChildrenRecursive(*t, t->root, root)) {
335 applyTree(host, std::move(root));
336 return true;
337 }
338 patchProps(host, t->root, std::move(root));
339 t->dirty = false;
340 t->transformDirty = true;
341 return false;
342}
343
344} // namespace eve::scene
LogicalId target
int root
Definition AnimSmr.cpp:119
building::EdgeCurveGroup group
scene::NodeDesc desc
glm::vec3 n
Definition Grass.cpp:63
std::int32_t c
std::string name
std::vector< std::int32_t > order
graphics::Canvas * previous
float d
float t
const RoadNode * node
bool found
int children
Definition TreeMesh.cpp:295
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
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
ECS mount point for one scene graph (full scene or nested subtree root). Isomorphic to eve::ui::UIHos...
Definition SceneHost.h:89
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
bool applyTreeReconcile(SceneHost *host, NodeDesc root)
Key-aware patch when structure matches; else full replace. Returns true if full rebuild.
Definition NodeDesc.cpp:323
void validateUniqueIds(const NodeDesc &root)
Definition NodeDesc.cpp:205
NodeDesc when(bool cond, NodeDesc child)
Conditional: include child only when cond is true (empty group otherwise).
Definition NodeDesc.cpp:234
void applyTree(SceneHost *host, NodeDesc root)
Applies tree.
Definition NodeDesc.cpp:243
NodeDesc whenElse(bool cond, NodeDesc ifTrue, NodeDesc ifFalse)
When else.
Definition NodeDesc.cpp:239
WidgetDesc child(std::string id, std::vector< WidgetDesc > children, float width, float height)
Scrollable child region with an explicit size.
Definition Widget.cpp:635
Declarative scene-node description (build once / on dirty → flatten into SceneHost::Tree)....
Definition NodeDesc.h:19
std::string id
Definition NodeDesc.h:20
const std::string & reconcileKey() const
Reconcile key.
Definition Widget.h:413