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