载入中...
搜索中...
未找到
Scene.cpp
浏览该文件的文档.
1#include "common/ECS.h"
3#include "scene/Scene.h"
5#include "scene/PcgGrowth.h"
6
8#include "scene/SceneBounds.h"
10#include "scene/SceneInternal.h"
12#include "scene/FollowPlayer.h"
14
15#include "spatial/Octree.h"
16
17#ifdef EVENGINE_SCENE_JSON
18#include <Poco/JSON/Array.h>
19#include <Poco/JSON/Object.h>
20#include <Poco/JSON/Parser.h>
21#include <Poco/JSON/Stringifier.h>
22#endif
23
24#include <simplesquirrel/simplesquirrel.hpp>
25
26#include <glm/glm.hpp>
27#include <glm/gtc/matrix_transform.hpp>
28#include <glm/gtc/quaternion.hpp>
29
30#include <cstring>
31#include <limits>
32#include <sstream>
33#include <utility>
34#include <stdexcept>
35
36namespace eve::scene {
37
42
44 if (vm_) {
45 for (auto &item : delayedDetaches_) sq_release(vm_, &item.instance);
46 }
47 delayedDetaches_.clear();
48}
49
51
52namespace {
53
54template <class T>
55T *borrowSceneResult(eve::Result<T *> result) {
56 if (!result.ok()) return nullptr;
57 return std::move(result).takeValue();
58}
59
60SceneHost *findHostByName(const std::string &name) {
61 if (name.empty()) return nullptr;
62 if (ecs::current()->getManager<SceneHost>() == nullptr) return nullptr;
63 auto view = ecs::View<SceneHost, SceneHost::Meta>();
64 for (auto it = view.begin(); it != view.end(); ++it) {
65 auto [meta] = *it;
66 if (meta->entity && meta->name == name) return meta->entity;
67 }
68 return nullptr;
69}
70
71SceneHost *findHostByOwnerId(uint32_t ownerId) {
72 if (ownerId == 0) return nullptr;
73 if (ecs::current()->getManager<SceneHost>() == nullptr) return nullptr;
74 auto view = ecs::View<SceneHost, SceneHost::Meta>();
75 for (auto it = view.begin(); it != view.end(); ++it) {
76 auto [meta] = *it;
77 if (meta->entity && meta->ownerId == ownerId) return meta->entity;
78 }
79 return nullptr;
80}
81
83void setStringField(HSQUIRRELVM vm, HSQOBJECT inst, const char *name,
84 const std::string &value) {
85 if (!vm || inst._type != OT_INSTANCE) return;
86 const SQInteger top = sq_gettop(vm);
87 sq_pushobject(vm, inst);
88 sq_pushstring(vm, name, -1);
89 sq_pushstring(vm, value.c_str(), -1);
90 if (SQ_FAILED(sq_set(vm, -3))) {
91 sq_settop(vm, top);
92 sq_pushobject(vm, inst);
93 sq_pushstring(vm, name, -1);
94 sq_pushstring(vm, value.c_str(), -1);
95 sq_newslot(vm, -3, SQFalse);
96 }
97 sq_settop(vm, top);
98}
99
100bool sameScriptObject(const HSQOBJECT &a, const HSQOBJECT &b) {
101 return a._type == b._type &&
102 std::memcmp(&a._unVal, &b._unVal, sizeof(a._unVal)) == 0;
103}
104
105// --- JSON serialization helpers ---
106
107#ifdef EVENGINE_SCENE_JSON
108Poco::JSON::Object::Ptr nodeToJson(const SceneHost::Tree &tree, const SceneNode *n) {
109 Poco::JSON::Object::Ptr o = new Poco::JSON::Object;
110 o->set("id", n->id);
111 if (!n->persistentId.isNil()) o->set("persistentId", n->persistentId.format());
112 o->set("key", n->key);
113 o->set("name", n->name);
114 o->set("space", n->space);
115 o->set("visible", n->visible);
116 o->set("layer", n->layer);
117 o->set("x", n->x);
118 o->set("y", n->y);
119 o->set("z", n->z);
120 o->set("yaw", n->yaw);
121 o->set("pitch", n->pitch);
122 o->set("roll", n->roll);
123 o->set("sx", n->sx);
124 o->set("sy", n->sy);
125 o->set("sz", n->sz);
126 if (n->hasBounds) {
127 o->set("bminX", n->bminX);
128 o->set("bminY", n->bminY);
129 o->set("bminZ", n->bminZ);
130 o->set("bmaxX", n->bmaxX);
131 o->set("bmaxY", n->bmaxY);
132 o->set("bmaxZ", n->bmaxZ);
133 }
134 if (!n->tags.empty()) {
135 Poco::JSON::Array::Ptr arr = new Poco::JSON::Array;
136 for (const auto &t : n->tags) arr->add(t);
137 o->set("tags", arr);
138 }
139 if (n->firstChild >= 0) {
140 Poco::JSON::Array::Ptr kids = new Poco::JSON::Array;
141 for (int c = n->firstChild; c >= 0;
142 c = tree.nodes[size_t(c)].nextSibling) {
143 kids->add(nodeToJson(tree, &tree.nodes[size_t(c)]));
144 }
145 o->set("children", kids);
146 }
147 return o;
148}
149
150NodeDesc nodeFromJson(const Poco::JSON::Object::Ptr &o) {
151 NodeDesc d;
152 d.id = o->optValue<std::string>("id", "");
153 const std::string persistentId = o->optValue<std::string>("persistentId", "");
154 if (!persistentId.empty()) {
155 if (const auto parsed = eve::SceneObjectId::parse(persistentId)) d.persistentId = *parsed;
156 }
157 d.key = o->optValue<std::string>("key", d.id);
158 d.name = o->optValue<std::string>("name", d.id);
159 d.space = o->optValue<std::string>("space", "3d");
160 d.visible = o->optValue<bool>("visible", true);
161 d.layer = o->optValue<int>("layer", 0);
162 d.x = float(o->optValue<double>("x", 0.0));
163 d.y = float(o->optValue<double>("y", 0.0));
164 d.z = float(o->optValue<double>("z", 0.0));
165 d.yaw = float(o->optValue<double>("yaw", 0.0));
166 d.pitch = float(o->optValue<double>("pitch", 0.0));
167 d.roll = float(o->optValue<double>("roll", 0.0));
168 d.sx = float(o->optValue<double>("sx", 1.0));
169 d.sy = float(o->optValue<double>("sy", 1.0));
170 d.sz = float(o->optValue<double>("sz", 1.0));
171 if (o->has("bminX")) {
172 d.bminX = float(o->optValue<double>("bminX", 0.0));
173 d.bminY = float(o->optValue<double>("bminY", 0.0));
174 d.bminZ = float(o->optValue<double>("bminZ", 0.0));
175 d.bmaxX = float(o->optValue<double>("bmaxX", 0.0));
176 d.bmaxY = float(o->optValue<double>("bmaxY", 0.0));
177 d.bmaxZ = float(o->optValue<double>("bmaxZ", 0.0));
178 d.hasBounds = true;
179 }
180 if (o->has("tags")) {
181 Poco::JSON::Array::Ptr arr = o->getArray("tags");
182 for (size_t i = 0; i < arr->size(); ++i) {
183 d.tags.push_back(arr->getElement<std::string>(static_cast<unsigned int>(i)));
184 }
185 }
186 if (o->has("children")) {
187 Poco::JSON::Array::Ptr kids = o->getArray("children");
188 for (size_t i = 0; i < kids->size(); ++i) {
189 d.children.push_back(nodeFromJson(kids->getObject(static_cast<unsigned int>(i))));
190 }
191 }
192 return d;
193}
194#endif
195
197const char *kSceneComponentScript = R"SQ(
198eve.SceneComponent <- class {
199 hostName = ""
200 dirty = true
201 forceFull = false
202 _scene = null
203 _mounted = false
204
205 constructor(sceneInstance = null) {
206 _scene = sceneInstance
207 hostName = ""
208 dirty = true
209 forceFull = false
210 _mounted = false
211 }
212
213 function setScene(sceneInstance) { _scene = sceneInstance }
214
215 function mountAs(name) {
216 hostName = name
217 dirty = true
218 forceFull = true
219 updateIfDirty()
220 }
221
222 function setState() { dirty = true }
223 function markDirty() { dirty = true }
224 function onMount() {}
225
226 // Override: call this.scene().beginNode / addNode / end ...
227 function build() {}
228
229 function scene() {
230 if (_scene != null) return _scene
231 try {
232 if (::scene != null) return ::scene
233 } catch (e) {}
234 _scene = ::eve.Scene()
235 return _scene
236 }
237
238 function updateIfDirty() {
239 if (!dirty) return false
240 local s = scene()
241 s.beginBuild()
242 build()
243 local name = hostName
244 if (name == null || name == "") name = "default"
245 if (forceFull) {
246 s.mountBuildAs(name)
247 forceFull = false
248 } else {
249 s.remountBuildAs(name)
250 }
251 if (!_mounted) {
252 _mounted = true
253 onMount()
254 }
255 dirty = false
256 return true
257 }
258
259 function rebuild(force = false) {
260 dirty = true
261 forceFull = force
262 return updateIfDirty()
263 }
264}
265)SQ";
266
267void injectSceneComponentClass(ssq::Table &eveTable) {
268 HSQUIRRELVM vm = eveTable.getHandle();
269 const SQInteger top = sq_gettop(vm);
270 if (SQ_FAILED(sq_compilebuffer(vm, kSceneComponentScript,
271 static_cast<SQInteger>(std::strlen(kSceneComponentScript)),
272 "SceneComponent.nut", SQTrue))) {
273 sq_settop(vm, top);
274 return;
275 }
276 sq_pushroottable(vm);
277 sq_call(vm, 1, SQFalse, SQTrue);
278 sq_settop(vm, top);
279}
280
285const char *kSceneEntityBaseScript = R"SQ(
286eve.SceneEntity <- class extends eve.Entity {
287 _scene = null
288 hostName = ""
289 nodeId = ""
290 _enabled = true
291
292 function scene() { return _scene }
293 function node() {
294 if (_scene == null) return null
295 return _scene.getNodeRefAt(hostName, nodeId)
296 }
297 function isEnabled() { return _enabled }
298 function setEnabled(enabled) {
299 local next = !!enabled
300 if (_enabled == next) return
301 _enabled = next
302 if (_enabled) onEnable()
303 else onDisable()
304 }
305 function enable() { setEnabled(true) }
306 function disable() { setEnabled(false) }
307 function onAttach() {}
308 function onDetach() {}
309 function onEnable() {}
310 function onDisable() {}
311 function update(dt) {}
312}
313)SQ";
314
319const char *kSceneEntityScript = R"SQ(
320// ---- eve.Scene: per-node entity API (script wrappers over native primitives) ----
321
322eve.Scene["getNodeRef"] <- function(nodeId, hostName = null) {
323 if (hostName == null) hostName = currentHostName()
324 return getNodeRefAt(hostName, nodeId) } eve.Scene["getNodeRefByPath"] <- function(path) {
325 return getNodeRefByPathAt(currentHostName(), path) } eve.Scene["attachEntity"] <- function(nodeId, cls) {
326 return attachEntityAt(currentHostName(), nodeId, cls) } eve.Scene["attachEntityAt"] <- function(hostName, nodeId, cls) {
327 if (typeof cls != "class") return null
328 if (!eve.isSubclass(cls, eve.Entity)) return null
329 local old = getEntityAt(hostName, nodeId, cls)
330 if (old != null) detachEntityAt(hostName, nodeId, old)
331 local inst = cls.create()
332 inst._scene = this
333 inst.hostName = hostName
334 inst.nodeId = nodeId
335 if (!rootEntity(hostName, nodeId, inst)) {
336 inst.destroy()
337 return null
338 }
339 inst.onAttach()
340 return inst
341}
342
343eve.Scene["detachEntity"] <- function(nodeId, inst) {
344 return detachEntityAt(currentHostName(), nodeId, inst) } eve.Scene["detachEntityAt"] <- function(hostName, nodeId, inst) {
345 if (inst == null) return false
346 local idx = -1
347 forEachEntity(hostName, nodeId, function(e, index) {
348 if (idx < 0 && e == inst) idx = index
349 })
350 if (idx < 0) return false
351 inst.onDetach()
352 inst.destroy()
353 return unrootEntityAt(hostName, nodeId, idx) } eve.Scene["scheduleDetachEntity"] <- function(nodeId, inst) {
354 // Native scheduleDetachEntityAt is bound on the class; this is the selected-host helper.
355 return scheduleDetachEntityAt(currentHostName(), nodeId, inst) } eve.Scene["collectIdsWith"] <- function(cls, hostName = null) {
356 if (hostName == null) hostName = currentHostName()
357 local out = []
358 foreach (id in collectIds()) {
359 if (hasEntityAt(hostName, id, cls)) out.push(id)
360 }
361 return out
362}
363
364eve.Scene["getEntity"] <- function(nodeId, cls) {
365 return getEntityAt(currentHostName(), nodeId, cls) } eve.Scene["getEntityAt"] <- function(hostName, nodeId, cls) {
366 local out = null
367 forEachEntity(hostName, nodeId, function(e) {
368 if (out == null && e instanceof cls) out = e
369 })
370 return out
371}
372
373eve.Scene["hasEntity"] <- function(nodeId, cls) {
374 return getEntityAt(currentHostName(), nodeId, cls) != null
375}
376
377eve.Scene["hasEntityAt"] <- function(hostName, nodeId, cls) {
378 return getEntityAt(hostName, nodeId, cls) != null
379}
380
381eve.Scene["entitiesOf"] <- function(nodeId) {
382 return entitiesOfAt(currentHostName(), nodeId) } eve.Scene["entitiesOfAt"] <- function(hostName, nodeId) {
383 local out = []
384 forEachEntity(hostName, nodeId, function(e) { out.push(e) })
385 return out
386}
387
388eve.Scene["update"] <- function(dt) {
389 updateScripts(dt)
390}
391
392// ---- eve.Scene: generic link system (selected-host convenience) ----
393
394eve.Scene["linkPhysics2D"] <- function(nodeId, body, mode = "node") {
395 return linkPhysics2DAt(currentHostName(), nodeId, body, mode) } eve.Scene["linkPhysics3D"] <- function(nodeId, body, mode = "node") {
396 return linkPhysics3DAt(currentHostName(), nodeId, body, mode) } eve.Scene["linkCamera3D"] <- function(nodeId, cam) {
397 return linkCamera3DAt(currentHostName(), nodeId, cam) } eve.Scene["linkAudio3D"] <- function(nodeId, source) {
398 return linkAudio3DAt(currentHostName(), nodeId, source) } eve.Scene["unlinkNodeKind"] <- function(nodeId, kind) {
399 return unlinkNodeKindAt(currentHostName(), nodeId, kind) } eve.Scene["linkCount"] <- function(nodeId) {
400 return linkCountAt(currentHostName(), nodeId)
401}
402
403// ---- eve.SceneNodeRef: node-level entity forwarding ----
404
405eve.SceneNodeRef["attachEntity"] <- function(cls) {
406 return getScene().attachEntityAt(getHostName(), getNodeId(), cls)
407}
408eve.SceneNodeRef["detachEntity"] <- function(inst) {
409 return getScene().detachEntityAt(getHostName(), getNodeId(), inst)
410}
411eve.SceneNodeRef["getEntity"] <- function(cls) {
412 return getScene().getEntityAt(getHostName(), getNodeId(), cls)
413}
414eve.SceneNodeRef["hasEntity"] <- function(cls) {
415 return getScene().hasEntityAt(getHostName(), getNodeId(), cls)
416}
417eve.SceneNodeRef["entitiesOf"] <- function() {
418 return getScene().entitiesOfAt(getHostName(), getNodeId())
419}
420eve.SceneNodeRef["scheduleDetachEntity"] <- function(inst) {
421 return getScene().scheduleDetachEntityAt(getHostName(), getNodeId(), inst)
422}
423
424// ---- eve.SceneNodeRef: generic link system ----
425
426eve.SceneNodeRef["linkRenderable2D"] <- function(r) {
427 return getScene().linkRenderable2DAt(getHostName(), getNodeId(), r)
428}
429eve.SceneNodeRef["linkRenderable3D"] <- function(r) {
430 return getScene().linkRenderable3DAt(getHostName(), getNodeId(), r)
431}
432eve.SceneNodeRef["linkPhysics2D"] <- function(body, mode = "node") {
433 return getScene().linkPhysics2DAt(getHostName(), getNodeId(), body, mode)
434}
435eve.SceneNodeRef["linkPhysics3D"] <- function(body, mode = "node") {
436 return getScene().linkPhysics3DAt(getHostName(), getNodeId(), body, mode)
437}
438eve.SceneNodeRef["linkCamera3D"] <- function(cam) {
439 return getScene().linkCamera3DAt(getHostName(), getNodeId(), cam)
440}
441eve.SceneNodeRef["linkAudio3D"] <- function(source) {
442 return getScene().linkAudio3DAt(getHostName(), getNodeId(), source)
443}
444eve.SceneNodeRef["unlinkNode"] <- function() {
445 return getScene().unlinkNodeAt(getHostName(), getNodeId())
446}
447eve.SceneNodeRef["unlinkNodeKind"] <- function(kind) {
448 return getScene().unlinkNodeKindAt(getHostName(), getNodeId(), kind)
449}
450eve.SceneNodeRef["linkCount"] <- function() {
451 return getScene().linkCountAt(getHostName(), getNodeId())
452}
453
454// ---- eve.Scene: script-API completeness (selected-host convenience) ----
455
456eve.Scene["_requireNode"] <- function(nodeId) {
457 if (!hasNode(nodeId)) throw "scene: node '" + nodeId + "' not found"
458}
459eve.Scene["getNodePosition"] <- function(nodeId) {
460 _requireNode(nodeId)
461 return getNodePositionAt(currentHostName(), nodeId) } eve.Scene["getNodeRotation"] <- function(nodeId) {
462 _requireNode(nodeId)
463 return getNodeRotationAt(currentHostName(), nodeId) } eve.Scene["getNodeScale"] <- function(nodeId) {
464 _requireNode(nodeId)
465 return getNodeScaleAt(currentHostName(), nodeId) } eve.Scene["getNodeVisible"] <- function(nodeId) {
466 _requireNode(nodeId)
467 return getNodeVisibleAt(currentHostName(), nodeId) } eve.Scene["getNodeWorldPosition"] <- function(nodeId) {
468 _requireNode(nodeId)
469 return getNodeWorldPositionAt(currentHostName(), nodeId) } eve.Scene["getNodeWorldRotation"] <- function(nodeId) {
470 _requireNode(nodeId)
471 return getNodeWorldRotationAt(currentHostName(), nodeId) } eve.Scene["getNodeWorldScale"] <- function(nodeId) {
472 _requireNode(nodeId)
473 return getNodeWorldScaleAt(currentHostName(), nodeId) } eve.Scene["localToWorld"] <- function(nodeId, x, y, z) {
474 _requireNode(nodeId)
475 return localToWorldAt(currentHostName(), nodeId, x, y, z) } eve.Scene["worldToLocal"] <- function(nodeId, x, y, z) {
476 _requireNode(nodeId)
477 return worldToLocalAt(currentHostName(), nodeId, x, y, z) } eve.Scene["setNodeParent"] <- function(nodeId, parentId) { return setNodeParentAt(currentHostName(), nodeId, parentId) }
478eve.Scene["removeNode"] <- function(nodeId) { return removeNodeAt(currentHostName(), nodeId) }
479eve.Scene["addNodeChild"] <- function(parentId, childId) { return addChildAt(currentHostName(), parentId, childId) }
480eve.Scene["removeNodeChild"] <- function(parentId, childId) { return removeChildAt(currentHostName(), parentId, childId) }
481eve.Scene["setNodeQuaternion"] <- function(nodeId, qx, qy, qz, qw) { return setNodeQuaternionAt(currentHostName(), nodeId, qx, qy, qz, qw) }
482eve.Scene["getNodeQuaternion"] <- function(nodeId) {
483 _requireNode(nodeId)
484 return getNodeQuaternionAt(currentHostName(), nodeId) } eve.Scene["setNodeLookAt"] <- function(nodeId, tx, ty, tz) { return setNodeLookAtAt(currentHostName(), nodeId, tx, ty, tz) }
485eve.Scene["addNodeTag"] <- function(nodeId, tag) { return addNodeTagAt(currentHostName(), nodeId, tag) }
486eve.Scene["removeNodeTag"] <- function(nodeId, tag) { return removeNodeTagAt(currentHostName(), nodeId, tag) }
487eve.Scene["hasNodeTag"] <- function(nodeId, tag) { return hasNodeTagAt(currentHostName(), nodeId, tag) }
488eve.Scene["getNodeTags"] <- function(nodeId) {
489 _requireNode(nodeId)
490 return getNodeTagsAt(currentHostName(), nodeId) } eve.Scene["collectIdsByTag"] <- function(tag) { return collectIdsByTagAt(currentHostName(), tag) }
491eve.Scene["setNodeLayer"] <- function(nodeId, layer) { return setNodeLayerAt(currentHostName(), nodeId, layer) }
492eve.Scene["getNodeLayer"] <- function(nodeId) {
493 _requireNode(nodeId)
494 return getNodeLayerAt(currentHostName(), nodeId) } eve.Scene["setNodeEventHandler"] <- function(cb) {
495 return setNodeEventHandlerAt(currentHostName(), cb)
496}
497
498// ---- eve.SceneNodeRef: script-API completeness forwarding ----
499
500eve.SceneNodeRef["_requireValid"] <- function() {
501 if (!isValid()) throw "scene: node not found (" + getHostName() + "/" + getNodeId() + ")"
502}
503eve.SceneNodeRef["localToWorld"] <- function(x, y, z) {
504 _requireValid()
505 return getScene().localToWorldAt(getHostName(), getNodeId(), x, y, z)
506}
507eve.SceneNodeRef["worldToLocal"] <- function(x, y, z) {
508 _requireValid()
509 return getScene().worldToLocalAt(getHostName(), getNodeId(), x, y, z)
510}
511eve.SceneNodeRef["setParent"] <- function(parentId) { return getScene().setNodeParentAt(getHostName(), getNodeId(), parentId) }
512eve.SceneNodeRef["removeNode"] <- function() { return getScene().removeNodeAt(getHostName(), getNodeId()) }
513eve.SceneNodeRef["setQuaternion"] <- function(qx, qy, qz, qw) { return getScene().setNodeQuaternionAt(getHostName(), getNodeId(), qx, qy, qz, qw) }
514eve.SceneNodeRef["getQuaternion"] <- function() {
515 _requireValid()
516 return getScene().getNodeQuaternionAt(getHostName(), getNodeId())
517}
518eve.SceneNodeRef["lookAt"] <- function(tx, ty, tz) { return getScene().setNodeLookAtAt(getHostName(), getNodeId(), tx, ty, tz) }
519eve.SceneNodeRef["addTag"] <- function(tag) { return getScene().addNodeTagAt(getHostName(), getNodeId(), tag) }
520eve.SceneNodeRef["removeTag"] <- function(tag) { return getScene().removeNodeTagAt(getHostName(), getNodeId(), tag) }
521eve.SceneNodeRef["hasTag"] <- function(tag) { return getScene().hasNodeTagAt(getHostName(), getNodeId(), tag) }
522eve.SceneNodeRef["getTags"] <- function() {
523 _requireValid()
524 return getScene().getNodeTagsAt(getHostName(), getNodeId())
525}
526eve.SceneNodeRef["setLayer"] <- function(layer) { return getScene().setNodeLayerAt(getHostName(), getNodeId(), layer) }
527eve.SceneNodeRef["getLayer"] <- function() {
528 _requireValid()
529 return getScene().getNodeLayerAt(getHostName(), getNodeId())
530}
531
532// ---- bounds / serialization / picking / culling ----
533
534eve.Scene["setNodeBounds"] <- function(nodeId, minX, minY, minZ, maxX, maxY, maxZ) {
535 return setNodeBoundsAt(currentHostName(), nodeId, minX, minY, minZ, maxX, maxY, maxZ) } eve.Scene["hasNodeBounds"] <- function(nodeId) {
536 return hasNodeBoundsAt(currentHostName(), nodeId) } eve.Scene["getNodeBounds"] <- function(nodeId) {
537 _requireNode(nodeId)
538 return getNodeBoundsAt(currentHostName(), nodeId) } eve.Scene["serializeHost"] <- function() {
539 return serializeHostAt(currentHostName()) } eve.Scene["deserializeHost"] <- function(json) {
540 return deserializeHostAt(currentHostName(), json) } eve.Scene["pickRay"] <- function(ox, oy, oz, dx, dy, dz) {
541 return pickRayAt(currentHostName(), ox, oy, oz, dx, dy, dz) } eve.Scene["pickScreen"] <- function(cam, screenX, screenY, viewW, viewH) {
542 return pickScreenAt(currentHostName(), cam, screenX, screenY, viewW, viewH) } eve.Scene["collectFrustumIds"] <- function(cam, viewW, viewH) {
543 return collectFrustumIdsAt(currentHostName(), cam, viewW, viewH) } eve.Scene["applyPcgTerrainCulling"] <- function(cam, viewW, viewH, tag) {
544 return applyPcgTerrainCullingAt(currentHostName(), cam, viewW, viewH, tag) } eve.Scene["syncSpatialIndex"] <- function(octree) {
545 return syncSpatialIndexAt(currentHostName(), octree) } eve.Scene["nodeIdFromSpatialId"] <- function(index) {
546 return nodeIdFromSpatialIdAt(currentHostName(), index)
547}
548
549eve.SceneNodeRef["setBounds"] <- function(minX, minY, minZ, maxX, maxY, maxZ) {
550 return getScene().setNodeBoundsAt(getHostName(), getNodeId(), minX, minY, minZ, maxX, maxY, maxZ)
551}
552eve.SceneNodeRef["hasBounds"] <- function() {
553 return getScene().hasNodeBoundsAt(getHostName(), getNodeId())
554}
555eve.SceneNodeRef["getBounds"] <- function() {
556 _requireValid()
557 return getScene().getNodeBoundsAt(getHostName(), getNodeId())
558}
559)SQ";
560
561void injectSceneEntityBase(ssq::Table &eveTable) {
562 HSQUIRRELVM vm = eveTable.getHandle();
563 const SQInteger top = sq_gettop(vm);
564 if (SQ_FAILED(sq_compilebuffer(vm, kSceneEntityBaseScript,
565 static_cast<SQInteger>(std::strlen(kSceneEntityBaseScript)),
566 "SceneEntity.nut", SQTrue))) {
567 sq_settop(vm, top);
568 return;
569 }
570 sq_pushroottable(vm);
571 sq_call(vm, 1, SQFalse, SQTrue);
572 sq_settop(vm, top);
573}
574
575void injectSceneEntityScript(ssq::Table &eveTable) {
576 HSQUIRRELVM vm = eveTable.getHandle();
577 const SQInteger top = sq_gettop(vm);
578 if (SQ_FAILED(sq_compilebuffer(vm, kSceneEntityScript,
579 static_cast<SQInteger>(std::strlen(kSceneEntityScript)),
580 "SceneEntity.nut", SQTrue))) {
581 sq_settop(vm, top);
582 return;
583 }
584 sq_pushroottable(vm);
585 sq_call(vm, 1, SQFalse, SQTrue);
586 sq_settop(vm, top);
587}
588
589[[maybe_unused]] const bool g_sceneEntityHookRegistered = []() {
590 eve::registerPostEcsHook([](ssq::Table &t) { injectSceneEntityBase(t); });
591 return true;
592}();
593
594} // namespace
595
596SceneHost *Scene::ensureSelected(const std::string &preferredName) {
597 if (selected_) return selected_;
598 if (!preferredName.empty()) {
599 selected_ = findHostByName(preferredName);
600 if (selected_) return selected_;
601 auto created = SceneHost::createHost(preferredName);
602 if (!created.ok()) return nullptr;
603 selected_ = std::move(created).takeValue();
604 return selected_;
605 }
606 auto created = SceneHost::createHost("default");
607 if (!created.ok()) return nullptr;
608 selected_ = std::move(created).takeValue();
609 return selected_;
610}
611
612eve::Result<SceneHost *> Scene::mountAs(const std::string &name, NodeDesc root) {
613 SceneHost *h = findHostByName(name);
614 if (!h) {
616 if (!created.ok()) return eve::Result<SceneHost *>::failure(created.status());
617 h = std::move(created).takeValue();
618 }
619 try {
620 h->setTree(std::move(root));
622 pruneOrphanObjects();
623 } catch (const std::exception &error) {
625 eve::DiagnosticCode::PreconditionViolation, std::string("scene mount rejected: ") + error.what(), "scene"));
626 }
627 selected_ = h;
629}
630
631eve::Result<SceneHost *> Scene::mount(NodeDesc root) { return mountAs("default", std::move(root)); }
632
634 SceneHost *h = selected_ ? selected_ : findHostByName("default");
635 if (!h) {
636 auto created = SceneHost::createHost("default");
637 if (!created.ok()) return eve::Result<SceneHost *>::failure(created.status());
638 h = std::move(created).takeValue();
639 }
640 try {
641 h->setTree(std::move(root));
643 pruneOrphanObjects();
644 } catch (const std::exception &error) {
647 std::string("scene remount rejected: ") + error.what(), "scene"));
648 }
649 selected_ = h;
651}
652
654 SceneHost *h = selected_ ? selected_ : findHostByName("default");
655 if (!h) {
656 auto created = SceneHost::createHost("default");
657 if (!created.ok()) return eve::Result<SceneHost *>::failure(created.status());
658 h = std::move(created).takeValue();
659 }
660 try {
661 const bool rebuilt = h->setTreeReconcile(std::move(root));
663 pruneOrphanObjects();
664 selected_ = h;
667 } catch (const std::exception &error) {
670 std::string("scene reconcile rejected: ") + error.what(), "scene"));
671 }
672}
673
674eve::Result<SceneHost *> Scene::remountAs(const std::string &name, NodeDesc root) {
675 return mountAs(name, std::move(root));
676}
677
678bool Scene::select(const std::string &name) {
679 SceneHost *h = findHostByName(name);
680 if (!h) return false;
681 selected_ = h;
682 return true;
683}
684
685eve::Result<SceneHost *> Scene::findHost(const std::string &name) const {
686 if (name.empty())
688 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "scene host name must not be empty", "scene"));
689 if (SceneHost *host = findHostByName(name)) return eve::Result<SceneHost *>::success(host, eve::Status::success());
691 eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "scene host was not found: " + name, "scene"));
692}
693
694eve::Result<SceneHost *> Scene::findHostByOwner(uint32_t ownerId) const {
695 if (ownerId == 0)
697 eve::DiagnosticCode::InvalidArgument, "scene host owner id must not be zero", "scene"));
698 if (SceneHost *host = findHostByOwnerId(ownerId))
701 eve::DiagnosticCode::NotFound, "scene host owner id was not found: " + std::to_string(ownerId), "scene"));
702}
703
704void Scene::bindOwner(uint32_t ownerId) {
705 SceneHost *h = ensureSelected();
706 h->setOwnerId(ownerId);
707}
708
710 if (auto *h = ensureSelected()) h->setVisible(visible);
712
713void Scene::setHostLayer(int layer) {
714 if (auto *h = ensureSelected()) h->setLayer(layer);
715}
716
718 if (selected_) TransformSystem::updateHost(selected_);
720}
721
723
724
725#ifdef EVENGINE_SCENE_JSON
726std::string Scene::serializeHostAt(const std::string &hostName) const {
727 SceneHost *h = resolveHost(hostName);
728 if (!h || !h->getRoot()) return "{}";
729 Poco::JSON::Object root;
730 root.set("host", h->getName());
731 root.set("root", nodeToJson(*h->tree(), h->getRoot()));
732 std::ostringstream oss;
733 Poco::JSON::Stringifier::stringify(root, oss);
734 return oss.str();
735}
736
737bool Scene::deserializeHostAt(const std::string &hostName, const std::string &json) {
738 try {
739 Poco::JSON::Parser parser;
740 Poco::Dynamic::Var result = parser.parse(json);
741 Poco::JSON::Object::Ptr root = result.extract<Poco::JSON::Object::Ptr>();
742 if (!root) return false;
743 Poco::JSON::Object::Ptr tree = root->getObject("root");
744 if (!tree) return false;
745 NodeDesc desc = nodeFromJson(tree);
746 auto mounted = mountAs(hostName.empty() ? "default" : hostName, std::move(desc));
747 return mounted.ok();
748 } catch (...) {
749 return false;
750 }
751}
752#else
753std::string Scene::serializeHostAt(const std::string &hostName) const {
754 (void)hostName;
755 return "{}"; // Poco JSON is not available on this build (e.g. WASM trim)
757
758bool Scene::deserializeHostAt(const std::string &hostName, const std::string &json) {
759 (void)hostName;
760 (void)json;
761 return false;
762}
763#endif
764
765void Scene::beginBuild() {
766 openStack_.clear();
767 hasBuiltRoot_ = false;
768 builtRoot_ = NodeDesc{};
769}
770
771void Scene::pushOpen(NodeDesc d) { openStack_.push_back(std::move(d)); }
773NodeDesc &Scene::currentParent() {
774 if (openStack_.empty()) throw std::runtime_error("scene: node outside beginNode/beginGroup");
775 return openStack_.back();
776}
777
778void Scene::beginNode(const std::string &id, const std::string &name) {
779 if (openStack_.empty() && hasBuiltRoot_) beginBuild();
780 pushOpen(node(id, {}, name));
781}
783void Scene::beginGroup(const std::string &id) {
784 if (openStack_.empty() && hasBuiltRoot_) beginBuild();
785 pushOpen(group({}, id));
786}
788void Scene::end() {
789 if (openStack_.empty()) throw std::runtime_error("scene: end() without begin");
790 NodeDesc finished = std::move(openStack_.back());
791 openStack_.pop_back();
792 if (openStack_.empty()) {
793 builtRoot_ = std::move(finished);
794 hasBuiltRoot_ = true;
795 } else {
796 openStack_.back().children.push_back(std::move(finished));
797 }
798}
799
800void Scene::addNode(const std::string &id, const std::string &name) {
801 currentParent().children.push_back(node(id, {}, name));
802}
803
804void Scene::setBuildPosition(float x, float y, float z) {
805 NodeDesc &d = currentParent();
806 d.x = x;
807 d.y = y;
808 d.z = z;
809}
810
811void Scene::setBuildRotation(float yaw, float pitch, float roll) {
812 NodeDesc &d = currentParent();
813 d.yaw = yaw;
814 d.pitch = pitch;
815 d.roll = roll;
816}
817
818void Scene::setBuildScale(float sx, float sy, float sz) {
819 NodeDesc &d = currentParent();
820 d.sx = sx;
821 d.sy = sy;
822 d.sz = sz;
823}
824
825void Scene::setBuildSpace(const std::string &space) { currentParent().space = space; }
826
827void Scene::setBuildVisible(bool visible) { currentParent().visible = visible; }
828
829bool Scene::buildComplete() const { return openStack_.empty() && hasBuiltRoot_; }
830
832 if (!buildComplete()) return false;
833 auto mounted = remount(std::move(builtRoot_));
834 if (!mounted.ok()) return false;
835 hasBuiltRoot_ = false;
836 builtRoot_ = NodeDesc{};
837 return true;
838}
839
840bool Scene::mountBuildAs(const std::string &name) {
841 if (!buildComplete()) return false;
842 auto mounted = mountAs(name, std::move(builtRoot_));
843 if (!mounted.ok()) return false;
844 hasBuiltRoot_ = false;
845 builtRoot_ = NodeDesc{};
846 return true;
847}
848
849bool Scene::remountBuildAs(const std::string &name) {
850 if (!buildComplete()) return false;
851 SceneHost *h = findHostByName(name);
852 if (!h) {
854 if (!created.ok()) return false;
855 h = std::move(created).takeValue();
857 h->setTreeReconcile(std::move(builtRoot_));
859 pruneOrphanObjects();
860 selected_ = h;
861 hasBuiltRoot_ = false;
862 builtRoot_ = NodeDesc{};
863 return true;
864}
865
866// ---------------------------------------------------------------------------
867// Per-node script entities (native primitives; script wrappers see expose())
868// ---------------------------------------------------------------------------
869
870SceneHost *Scene::resolveHost(const std::string &hostName) const {
871 if (hostName.empty()) return selected_;
872 return findHostByName(hostName);
873}
874
875SceneObject *Scene::findSceneObjectById(uint32_t id) const {
876 if (id == 0) return nullptr;
877 if (ecs::current()->getManager<SceneObject>() == nullptr) return nullptr;
878 auto view = ecs::View<SceneObject, SceneObject::Meta>();
879 for (auto it = view.begin(); it != view.end(); ++it) {
880 auto [meta] = *it;
881 if (meta->entity && uint32_t(meta->entity->id) == id) return meta->entity;
883 return nullptr;
884}
885
886SceneObject *Scene::findSceneObjectByPersistentId(eve::SceneObjectId id) const {
887 if (id.isNil() || ecs::current()->getManager<SceneObject>() == nullptr) return nullptr;
888 auto view = ecs::View<SceneObject, SceneObject::Meta>();
889 for (auto it = view.begin(); it != view.end(); ++it) {
890 auto [meta] = *it;
891 if (meta->entity && meta->persistentId == id) return meta->entity;
892 }
893 return nullptr;
894}
895
896SceneObject *Scene::ensureSceneObject(SceneHost *host, SceneNode *node,
897 const std::string &hostName) {
898 if (node->objectId != 0) {
899 if (SceneObject *o = findSceneObjectById(node->objectId)) {
900 if (!node->persistentId.isNil()) o->meta()->persistentId = node->persistentId;
901 return o;
902 }
903 node->objectId = 0; // stale id (entity was released)
904 }
905 if (!node->persistentId.isNil()) {
906 if (SceneObject *o = findSceneObjectByPersistentId(node->persistentId)) {
907 node->objectId = uint32_t(o->id);
908 o->meta()->hostName = hostName;
909 o->meta()->nodeId = node->id;
910 return o;
911 }
912 }
913 auto created = SceneObject::createObject(hostName, node->id, node->persistentId);
914 if (!created.ok()) return nullptr;
915 SceneObject *o = std::move(created).takeValue();
916 node->objectId = uint32_t(o->id);
917 return o;
919
920bool Scene::rootEntity(const std::string &hostName, const std::string &nodeId,
921 ssq::Object instance) {
922 if (!vm_) return false;
923 SceneHost *h = resolveHost(hostName);
924 if (!h) return false;
925 SceneNode *n = borrowSceneResult(h->findById(nodeId));
926 if (!n) return false;
927 HSQOBJECT raw = instance.getRaw();
928 if (raw._type != OT_INSTANCE) return false;
929 const std::string hName = hostName.empty() ? h->getName() : hostName;
930 SceneObject *obj = ensureSceneObject(h, n, hName);
931 if (!obj) return false;
932 for (const auto &b : obj->scriptBindings()->instances) {
933 if (sameScriptObject(b, raw)) return false; // already rooted
934 }
935 sq_addref(vm_, &raw);
936 obj->scriptBindings()->instances.push_back(raw);
937 return true;
938}
939
940bool Scene::unrootEntityAt(const std::string &hostName, const std::string &nodeId,
941 int index) {
942 if (!vm_) return false;
943 SceneHost *h = resolveHost(hostName);
944 if (!h) return false;
945 SceneNode *n = borrowSceneResult(h->findById(nodeId));
946 if (!n) return false;
947 SceneObject *obj = n->objectId ? findSceneObjectById(n->objectId) : nullptr;
948 if (!obj) return false;
949 auto &vec = obj->scriptBindings()->instances;
950 if (index < 0 || index >= int(vec.size())) return false;
951 HSQOBJECT raw = vec[size_t(index)];
952 vec.erase(vec.begin() + index);
953 sq_release(vm_, &raw);
954 if (vec.empty()) {
955 n->objectId = 0;
956 obj->release();
957 }
958 return true;
959}
960
961int Scene::forEachEntity(const std::string &hostName, const std::string &nodeId,
962 ssq::Object cb) {
963 if (!vm_) return 0;
964 SceneHost *h = resolveHost(hostName);
965 if (!h) return 0;
966 SceneNode *n = borrowSceneResult(h->findById(nodeId));
967 if (!n) return 0;
968 SceneObject *obj = n->objectId ? findSceneObjectById(n->objectId) : nullptr;
969 if (!obj) return 0;
970 // Copy + addref: the callback may attach/detach and mutate the live list.
971 auto vec = obj->scriptBindings()->instances;
972 for (auto &b : vec) sq_addref(vm_, &b);
973 HSQOBJECT fn = cb.getRaw();
974 sq_addref(vm_, &fn);
975 int count = 0;
976 for (size_t i = 0; i < vec.size(); ++i) {
977 if (callCallback(fn, vec[i], int(i))) ++count;
978 }
979 sq_release(vm_, &fn);
980 for (auto &b : vec) sq_release(vm_, &b);
981 return count;
982}
983
984void Scene::updateScripts(float dt) {
986 if (!vm_) return;
987 if (ecs::current()->getManager<SceneObject>() == nullptr) return;
988 // Snapshot + addref so script update(dt) may attach/detach safely.
989 std::vector<HSQOBJECT> snapshot;
990 {
991 auto view = ecs::View<SceneObject, SceneObject::Meta, SceneObject::ScriptBindings>();
992 for (auto it = view.begin(); it != view.end(); ++it) {
993 auto [meta, sb] = *it;
994 if (!meta->entity) continue;
995 for (auto &inst : sb->instances) snapshot.push_back(inst);
996 }
997 }
998 for (auto &h : snapshot) sq_addref(vm_, &h);
999 for (auto &h : snapshot) {
1000 // Skip disabled entities; enable/disable is a script-side flag on SceneEntity.
1001 const SQInteger top = sq_gettop(vm_);
1002 sq_pushobject(vm_, h);
1003 sq_pushstring(vm_, "_enabled", -1);
1004 bool enabled = true;
1005 if (SQ_SUCCEEDED(sq_get(vm_, -2))) {
1006 SQBool flag = SQTrue;
1007 if (SQ_SUCCEEDED(sq_getbool(vm_, -1, &flag))) enabled = flag != SQFalse;
1008 }
1009 sq_settop(vm_, top);
1010 if (enabled) callMethod(h, "update", dt);
1011 }
1012 for (auto &h : snapshot) sq_release(vm_, &h);
1014}
1015
1016eve::Result<void> Scene::scheduleDetachEntityAt(const std::string &hostName,
1017 const std::string &nodeId,
1018 ssq::Object instance) {
1019 if (!vm_) {
1021 eve::DiagnosticCode::PreconditionViolation, "scene script VM is not ready", "scene"));
1022 }
1023 HSQOBJECT raw = instance.getRaw();
1024 if (raw._type != OT_INSTANCE) {
1026 eve::DiagnosticCode::InvalidArgument, "scheduleDetachEntityAt expects a script instance",
1027 "instance"));
1028 }
1029 SceneHost *h = resolveHost(hostName);
1030 if (!h) {
1032 eve::DiagnosticCode::NotFound, "scene host was not found", "hostName"));
1033 }
1034 SceneNode *n = borrowSceneResult(h->findById(nodeId));
1035 if (!n || !n->objectId) {
1037 eve::DiagnosticCode::NotFound, "scene node was not found", "nodeId"));
1038 }
1039 SceneObject *obj = findSceneObjectById(n->objectId);
1040 if (!obj) {
1042 eve::DiagnosticCode::NotFound, "scene object was not found", "nodeId"));
1043 }
1044 bool found = false;
1045 for (const auto &b : obj->scriptBindings()->instances) {
1046 if (sameScriptObject(b, raw)) {
1047 found = true;
1048 break;
1049 }
1050 }
1051 if (!found) {
1053 eve::DiagnosticCode::NotFound, "script entity is not rooted on that node", "instance"));
1054 }
1055 for (const auto &pending : delayedDetaches_) {
1056 if (pending.hostName == (hostName.empty() ? h->getName() : hostName) && pending.nodeId == nodeId &&
1057 sameScriptObject(pending.instance, raw))
1058 return eve::Result<void>::success(eve::Status::success(eve::StatusCode::NoOp));
1059 }
1060 DelayedDetach item;
1061 item.hostName = hostName.empty() ? h->getName() : hostName;
1062 item.nodeId = nodeId;
1063 item.instance = raw;
1064 sq_addref(vm_, &item.instance);
1065 delayedDetaches_.push_back(item);
1067}
1068
1070 if (!vm_ || delayedDetaches_.empty() || flushingDelayedDetaches_) return;
1071 flushingDelayedDetaches_ = true;
1072 std::vector<DelayedDetach> pending;
1073 pending.swap(delayedDetaches_);
1074 for (auto &item : pending) {
1075 // Reuse the script detach path so onDetach/destroy/unroot stay consistent.
1076 // Locate index then mirror detachEntityAt.
1077 SceneHost *h = resolveHost(item.hostName);
1078 SceneNode *n = h ? borrowSceneResult(h->findById(item.nodeId)) : nullptr;
1079 SceneObject *obj = (n && n->objectId) ? findSceneObjectById(n->objectId) : nullptr;
1080 if (obj) {
1081 auto &vec = obj->scriptBindings()->instances;
1082 for (size_t i = 0; i < vec.size(); ++i) {
1083 if (!sameScriptObject(vec[i], item.instance)) continue;
1084 callMethod0(item.instance, "onDetach");
1085 callMethod0(item.instance, "destroy");
1086 unrootEntityAt(item.hostName, item.nodeId, int(i));
1087 break;
1088 }
1089 }
1090 sq_release(vm_, &item.instance);
1091 }
1092 flushingDelayedDetaches_ = false;
1093 // Nested scheduleDetach during flush is drained on the next updateScripts.
1095
1096std::string Scene::currentHostName() const {
1097 return selected_ ? selected_->getName() : std::string{};
1098}
1099
1100SceneNodeRef *Scene::getNodeRefAt(const std::string &hostName,
1101 const std::string &nodeId) const {
1102 std::string h = hostName.empty()
1103 ? (selected_ ? selected_->getName() : std::string{})
1104 : hostName;
1105 return new SceneNodeRef(std::move(h), nodeId);
1106}
1107
1108SceneNodeRef *Scene::getNodeRefByPathAt(const std::string &hostName,
1109 const std::string &path) const {
1110 std::string h = hostName.empty()
1111 ? (selected_ ? selected_->getName() : std::string{})
1112 : hostName;
1113 SceneHost *host = resolveHost(hostName);
1114 if (!host) return new SceneNodeRef(std::move(h), "");
1115 SceneNode *n = borrowSceneResult(host->findByPath(path));
1116 return new SceneNodeRef(std::move(h), n ? n->id : std::string{});
1117}
1118
1119void Scene::pruneOrphanObjects() {
1120 if (ecs::current()->getManager<SceneObject>() == nullptr) return;
1121 std::vector<SceneObject *> orphans;
1122 {
1123 auto view = ecs::View<SceneObject, SceneObject::Meta, SceneObject::ScriptBindings>();
1124 for (auto it = view.begin(); it != view.end(); ++it) {
1125 auto [meta, sb] = *it;
1126 if (!meta->entity) continue;
1127 SceneHost *h = findHostByName(meta->hostName);
1128 if (!h) {
1129 orphans.push_back(meta->entity);
1130 continue;
1131 }
1132 SceneNode *n = borrowSceneResult(h->findById(meta->nodeId));
1133 if (!n || n->objectId != uint32_t(meta->entity->id)) {
1134 orphans.push_back(meta->entity);
1135 continue;
1136 }
1137 if (!n->persistentId.isNil()) meta->persistentId = n->persistentId;
1138 (void)sb;
1139 // Self-heal: node id / host renamed (reconcile patch), keep binding.
1140 if (meta->hostName != h->getName() || meta->nodeId != n->id) {
1141 meta->hostName = h->getName();
1142 meta->nodeId = n->id;
1143 syncBindingRefs(meta->entity);
1144 }
1145 }
1146 }
1147 for (SceneObject *o : orphans) teardownBindings(o);
1148}
1149
1150void Scene::syncBindingRefs(SceneObject *obj) {
1151 if (!vm_ || !obj) return;
1152 for (auto &inst : obj->scriptBindings()->instances) {
1153 setStringField(vm_, inst, "hostName", obj->meta()->hostName);
1154 setStringField(vm_, inst, "nodeId", obj->meta()->nodeId);
1155 }
1156}
1157
1158void Scene::teardownBindings(SceneObject *obj) {
1159 if (!obj) return;
1160 auto &vec = obj->scriptBindings()->instances;
1161 if (vm_) {
1162 for (auto &inst : vec) {
1163 callMethod0(inst, "onDetach");
1164 callMethod0(inst, "destroy");
1165 sq_release(vm_, &inst);
1166 }
1167 }
1168 vec.clear();
1169 obj->release();
1170}
1171
1172bool Scene::callMethod(HSQOBJECT inst, const char *name, float dt) {
1173 if (!vm_ || inst._type != OT_INSTANCE) return false;
1174 const SQInteger top = sq_gettop(vm_);
1175 sq_pushobject(vm_, inst);
1176 sq_pushstring(vm_, name, -1);
1177 if (SQ_FAILED(sq_get(vm_, -2))) {
1178 sq_settop(vm_, top);
1179 return false;
1180 }
1181 const SQObjectType t = sq_gettype(vm_, -1);
1182 if (t != OT_CLOSURE && t != OT_NATIVECLOSURE) {
1183 sq_settop(vm_, top);
1184 return false;
1185 }
1186 sq_pushobject(vm_, inst); // this
1187 sq_pushfloat(vm_, SQFloat(dt));
1188 if (SQ_FAILED(sq_call(vm_, 2, SQFalse, SQTrue))) {
1189 sq_settop(vm_, top);
1190 return false;
1191 }
1192 sq_settop(vm_, top);
1193 return true;
1194}
1195
1196bool Scene::callMethod0(HSQOBJECT inst, const char *name) {
1197 if (!vm_ || inst._type != OT_INSTANCE) return false;
1198 const SQInteger top = sq_gettop(vm_);
1199 sq_pushobject(vm_, inst);
1200 sq_pushstring(vm_, name, -1);
1201 if (SQ_FAILED(sq_get(vm_, -2))) {
1202 sq_settop(vm_, top);
1203 return false;
1204 }
1205 const SQObjectType t = sq_gettype(vm_, -1);
1206 if (t != OT_CLOSURE && t != OT_NATIVECLOSURE) {
1207 sq_settop(vm_, top);
1208 return false;
1209 }
1210 sq_pushobject(vm_, inst); // this
1211 if (SQ_FAILED(sq_call(vm_, 1, SQFalse, SQTrue))) {
1212 sq_settop(vm_, top);
1213 return false;
1214 }
1215 sq_settop(vm_, top);
1216 return true;
1217}
1218
1219bool Scene::callCallback(HSQOBJECT fn, HSQOBJECT inst, int index) {
1220 if (!vm_) return false;
1221 const SQInteger top = sq_gettop(vm_);
1222 sq_pushobject(vm_, fn);
1223 sq_pushobject(vm_, inst);
1224 sq_pushinteger(vm_, SQInteger(index));
1225 if (SQ_FAILED(sq_call(vm_, 2, SQFalse, SQTrue))) {
1226 sq_settop(vm_, top);
1227 return false;
1228 }
1229 sq_settop(vm_, top);
1230 return true;
1231}
1232
1233void Scene::expose(ssq::Table &table) {
1234 if (Scene *self = Scene::create()) self->vm_ = table.getHandle();
1235 auto cls = table.addClass(name, Scene::create, false);
1236 expose(cls);
1237
1238 // Lightweight node handle (owned by script; created via getNodeRef*).
1239 auto refCls = table.addClass<SceneNodeRef>(
1240 "SceneNodeRef",
1241 std::function<SceneNodeRef *()>([]() -> SceneNodeRef * { return nullptr; }), true);
1242 refCls.addFunc("getNodeId", &SceneNodeRef::getNodeId);
1243 refCls.addFunc("getHostName", &SceneNodeRef::getHostName);
1244 refCls.addFunc("getPersistentId",
1245 [](SceneNodeRef *ref) { return ref ? ref->persistentId().format() : std::string{}; });
1246 refCls.addFunc("isValid", &SceneNodeRef::isValid);
1247 refCls.addFunc("getScene", &SceneNodeRef::getScene);
1248 refCls.addFunc("setPosition", &SceneNodeRef::setPosition);
1249 refCls.addFunc("getPositionX", &SceneNodeRef::getPositionX);
1250 refCls.addFunc("getPositionY", &SceneNodeRef::getPositionY);
1251 refCls.addFunc("getPositionZ", &SceneNodeRef::getPositionZ);
1252 refCls.addFunc("getPosition", &SceneNodeRef::getPosition);
1253 refCls.addFunc("setRotation", &SceneNodeRef::setRotation);
1254 refCls.addFunc("getRotationYaw", &SceneNodeRef::getRotationYaw);
1255 refCls.addFunc("getRotationPitch", &SceneNodeRef::getRotationPitch);
1256 refCls.addFunc("getRotationRoll", &SceneNodeRef::getRotationRoll);
1257 refCls.addFunc("getRotation", &SceneNodeRef::getRotation);
1258 refCls.addFunc("setScale", &SceneNodeRef::setScale);
1259 refCls.addFunc("getScaleX", &SceneNodeRef::getScaleX);
1260 refCls.addFunc("getScaleY", &SceneNodeRef::getScaleY);
1261 refCls.addFunc("getScaleZ", &SceneNodeRef::getScaleZ);
1262 refCls.addFunc("getScale", &SceneNodeRef::getScale);
1263 refCls.addFunc("setVisible", &SceneNodeRef::setVisible);
1264 refCls.addFunc("isVisible", &SceneNodeRef::isVisible);
1265 refCls.addFunc("getWorldPositionX", &SceneNodeRef::getWorldPositionX);
1266 refCls.addFunc("getWorldPositionY", &SceneNodeRef::getWorldPositionY);
1267 refCls.addFunc("getWorldPositionZ", &SceneNodeRef::getWorldPositionZ);
1268 refCls.addFunc("getWorldPosition", &SceneNodeRef::getWorldPosition);
1269 refCls.addFunc("getWorldMatrix", &SceneNodeRef::getWorldMatrix);
1270 refCls.addFunc("getForward", &SceneNodeRef::getForward);
1271 refCls.addFunc("getRight", &SceneNodeRef::getRight);
1272 refCls.addFunc("getUp", &SceneNodeRef::getUp);
1273 refCls.addFunc("getParentId", &SceneNodeRef::getParentId);
1274 refCls.addFunc("getChildCount", &SceneNodeRef::getChildCount);
1275 refCls.addFunc("getChildIdAt", &SceneNodeRef::getChildIdAt);
1276 refCls.addFunc("getPath", &SceneNodeRef::getPath);
1277
1278 injectSceneComponentClass(table);
1279 injectSceneEntityScript(table);
1284}
1285
1286void Scene::expose(ssq::Class &cls) {
1287 cls.addFunc("getName", &Scene::getName);
1288 cls.addFunc("select", &Scene::select);
1289 cls.addFunc("bindOwner", &Scene::bindOwner);
1290 cls.addFunc("setHostVisible", &Scene::setHostVisible);
1291 cls.addFunc("setHostLayer", &Scene::setHostLayer);
1292 cls.addFunc("currentHostName", &Scene::currentHostName);
1293 cls.addFunc("updateTransforms", &Scene::updateTransforms);
1294 cls.addFunc("updateTransformsAll", &Scene::updateTransformsAll);
1295 cls.addFunc("updateScripts", &Scene::updateScripts);
1296 cls.addFunc("setNodePosition", &Scene::setNodePosition);
1297 cls.addFunc("setNodeRotation", &Scene::setNodeRotation);
1298 cls.addFunc("setNodeScale", &Scene::setNodeScale);
1299 cls.addFunc("setNodeVisible", &Scene::setNodeVisible);
1300 cls.addFunc("linkRenderable2D", &Scene::linkRenderable2D);
1301 cls.addFunc("linkRenderable3D", &Scene::linkRenderable3D);
1302 cls.addFunc("unlinkNode", &Scene::unlinkNode);
1303
1304 // Generic link system primitives (host-scoped; script wrappers below)
1305 cls.addFunc("linkRenderable2DAt", &Scene::linkRenderable2DAt);
1306 cls.addFunc("linkRenderable3DAt", &Scene::linkRenderable3DAt);
1307 cls.addFunc("linkPhysics2DAt", &Scene::linkPhysics2DAt);
1308 cls.addFunc("linkPhysics3DAt", &Scene::linkPhysics3DAt);
1309 cls.addFunc("linkCamera3DAt", &Scene::linkCamera3DAt);
1310 cls.addFunc("linkAudio3DAt", &Scene::linkAudio3DAt);
1311 cls.addFunc("unlinkNodeAt", &Scene::unlinkNodeAt);
1312 cls.addFunc("unlinkNodeKindAt", &Scene::unlinkNodeKindAt);
1313 cls.addFunc("linkCountAt", &Scene::linkCountAt);
1314
1315 // Script-API completeness primitives (host-scoped)
1316 cls.addFunc("getNodePositionAt", &Scene::getNodePositionAt);
1317 cls.addFunc("getNodeRotationAt", &Scene::getNodeRotationAt);
1318 cls.addFunc("getNodeScaleAt", &Scene::getNodeScaleAt);
1319 cls.addFunc("getNodeVisibleAt", &Scene::getNodeVisibleAt);
1320 cls.addFunc("getNodeWorldPositionAt", &Scene::getNodeWorldPositionAt);
1321 cls.addFunc("getNodeWorldRotationAt", &Scene::getNodeWorldRotationAt);
1322 cls.addFunc("getNodeWorldScaleAt", &Scene::getNodeWorldScaleAt);
1323 cls.addFunc("localToWorldAt", &Scene::localToWorldAt);
1324 cls.addFunc("worldToLocalAt", &Scene::worldToLocalAt);
1325 cls.addFunc("setNodeParentAt", &Scene::setNodeParentAt);
1326 cls.addFunc("removeNodeAt", &Scene::removeNodeAt);
1327 cls.addFunc("addChildAt", &Scene::addChildAt);
1328 cls.addFunc("removeChildAt", &Scene::removeChildAt);
1329 cls.addFunc("setNodeQuaternionAt", &Scene::setNodeQuaternionAt);
1330 cls.addFunc("getNodeQuaternionAt", &Scene::getNodeQuaternionAt);
1331 cls.addFunc("setNodeLookAtAt", &Scene::setNodeLookAtAt);
1332 cls.addFunc("addNodeTagAt", &Scene::addNodeTagAt);
1333 cls.addFunc("removeNodeTagAt", &Scene::removeNodeTagAt);
1334 cls.addFunc("hasNodeTagAt", &Scene::hasNodeTagAt);
1335 cls.addFunc("getNodeTagsAt", &Scene::getNodeTagsAt);
1336 cls.addFunc("collectIdsByTagAt", &Scene::collectIdsByTagAt);
1337 cls.addFunc("setNodeLayerAt", &Scene::setNodeLayerAt);
1338 cls.addFunc("getNodeLayerAt", &Scene::getNodeLayerAt);
1339 cls.addFunc("setNodeEventHandlerAt",
1340 [](Scene *self, std::string hostName, ssq::Object cb) {
1341 return self->setNodeEventHandlerAt(hostName, cb);
1342 });
1343
1344 // Bounds / serialization / picking / culling
1345 cls.addFunc("setNodeBoundsAt", &Scene::setNodeBoundsAt);
1346 cls.addFunc("hasNodeBoundsAt", &Scene::hasNodeBoundsAt);
1347 cls.addFunc("getNodeBoundsAt", &Scene::getNodeBoundsAt);
1348 cls.addFunc("serializeHostAt", &Scene::serializeHostAt);
1349 cls.addFunc("deserializeHostAt", &Scene::deserializeHostAt);
1350 cls.addFunc("pickRayAt", &Scene::pickRayAt);
1351 cls.addFunc("pickScreenAt", &Scene::pickScreenAt);
1352 cls.addFunc("collectFrustumIdsAt", &Scene::collectFrustumIdsAt);
1353 cls.addFunc("applyPcgTerrainCullingAt", [vm=cls.getHandle()](Scene* self,const std::string& host,
1354 graphics::Camera3D* cam,float w,float h,const std::string& tag) {
1355 return eve::script::projectResult(vm,self->applyPcgTerrainCullingAt(host,cam,w,h,tag),
1356 [](int changed){return Value(changed);});
1357 });
1358 cls.addFunc("syncSpatialIndexAt", &Scene::syncSpatialIndexAt);
1359 cls.addFunc("nodeIdFromSpatialIdAt", &Scene::nodeIdFromSpatialIdAt);
1360
1361 // Per-node script entity primitives (called from injected script wrappers).
1362 // ssq::Object params are bound via lambdas (member-pointer path may not
1363 // support raw script objects; lambda path is used by eve.component too).
1364 cls.addFunc("rootEntity",
1365 [](Scene *self, std::string hostName, std::string nodeId,
1366 ssq::Object instance) {
1367 return self->rootEntity(hostName, nodeId, instance);
1368 });
1369 cls.addFunc("unrootEntityAt", &Scene::unrootEntityAt);
1370 cls.addFunc("scheduleDetachEntityAt",
1371 [](Scene *self, std::string hostName, std::string nodeId,
1372 ssq::Object instance) {
1373 // Script surface stays bool (parity with attach/detach helpers);
1374 // native API is Result<void> for architecture api-shape.
1375 return self->scheduleDetachEntityAt(hostName, nodeId, instance).ok();
1376 });
1377 cls.addFunc("forEachEntity",
1378 [](Scene *self, std::string hostName, std::string nodeId,
1379 ssq::Object cb) {
1380 return self->forEachEntity(hostName, nodeId, cb);
1381 });
1382 cls.addFunc("getNodeRefAt", &Scene::getNodeRefAt);
1383 cls.addFunc("getNodeRefByPathAt", &Scene::getNodeRefByPathAt);
1384
1385 cls.addFunc("hasNode", &Scene::hasNode);
1386 cls.addFunc("getNodeCount", &Scene::getNodeCount);
1387 cls.addFunc("getRootId", &Scene::getRootId);
1388 cls.addFunc("getParentId", &Scene::getParentId);
1389 cls.addFunc("getChildCount", &Scene::getChildCount);
1390 cls.addFunc("getChildIdAt", &Scene::getChildIdAt);
1391 cls.addFunc("findIdByName", &Scene::findIdByName);
1392 cls.addFunc("findIdByPath", &Scene::findIdByPath);
1393 cls.addFunc("getNodePath", &Scene::getNodePath);
1394 cls.addFunc("isAncestor", &Scene::isAncestor);
1395 cls.addFunc("isDescendant", &Scene::isDescendant);
1396 cls.addFunc("collectIds", &Scene::collectIds);
1397 cls.addFunc("collectIdsFrom", &Scene::collectIdsFrom);
1398 cls.addFunc("collectIdsByName", &Scene::collectIdsByName);
1399 cls.addFunc("collectIdsVisible", &Scene::collectIdsVisible);
1400 cls.addFunc("collectChildIds", &Scene::collectChildIds);
1401 cls.addFunc("walkDepthFirstIds", &Scene::walkDepthFirstIds);
1402 cls.addFunc("walkBreadthFirstIds", &Scene::walkBreadthFirstIds);
1403
1404 cls.addFunc("beginBuild", &Scene::beginBuild);
1405 cls.addFunc("beginNode", &Scene::beginNode);
1406 cls.addFunc("beginGroup", &Scene::beginGroup);
1407 cls.addFunc("end", &Scene::end);
1408 cls.addFunc("addNode", &Scene::addNode);
1409 cls.addFunc("setBuildPosition", &Scene::setBuildPosition);
1410 cls.addFunc("setBuildRotation", &Scene::setBuildRotation);
1411 cls.addFunc("setBuildScale", &Scene::setBuildScale);
1412 cls.addFunc("setBuildSpace", &Scene::setBuildSpace);
1413 cls.addFunc("setBuildVisible", &Scene::setBuildVisible);
1414 cls.addFunc("mountBuild", &Scene::mountBuild);
1415 cls.addFunc("mountBuildAs", &Scene::mountBuildAs);
1416 cls.addFunc("remountBuildAs", &Scene::remountBuildAs);
1417}
1418
1419} // namespace eve::scene
1420
double value
SQInteger top
float w
Definition AnimClip.cpp:738
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< QuestEvent > pending
building::EdgeCurveGroup group
std::vector< BuildingInstanceSnapshot > instances
struct SQVM * HSQUIRRELVM
std::string nodeId
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
scene::NodeDesc desc
glm::vec3 n
Definition Grass.cpp:63
std::int32_t c
int h
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
const std::string * tag
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
std::string error
Definition Package.cpp:60
TileLayer * layer
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
float d
float t
glm::mat4 view
const RoadNode * node
bool found
int created
std::string string
std::uint32_t count
The single Squirrel projection for common Result, Status and Value.
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
virtual std::string getName() const =0
Returns the name.
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
Id128 public API.
Definition Identity.h:112
static std::optional< Id128 > parse(std::string_view text) noexcept
Parses canonical UUID text.
Definition Identity.h:151
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< SceneHost * > createHost(const std::string &name="")
Creates an ECS-owned scene host with an explicit Result contract.
const std::string & getName()
float getPositionX() const
Returns the position x.
float getWorldPositionZ() const
Returns the world position z.
std::vector< float > getForward() const
Normalized local axes in world space (forward = +Z).
bool isValid() const
True when host + node currently resolve.
std::string getHostName() const
Returns the host name.
float getWorldPositionX() const
Returns the world position x.
float getRotationYaw() const
Returns the rotation yaw.
float getWorldPositionY() const
Returns the world position y.
float getRotationPitch() const
Returns the rotation pitch.
bool setVisible(bool v)
Sets the visible.
std::string getNodeId() const
Returns the node id.
float getScaleZ() const
Returns the scale z.
float getScaleX() const
Returns the scale x.
Scene * getScene() const
Returns the eve.Scene module instance for entity-binding forwarding.
std::string getPath() const
Returns the path.
std::string getChildIdAt(int ordinal) const
Returns the child id at.
std::vector< float > getPosition() const
Returns the position.
std::vector< float > getWorldPosition() const
Returns the world position.
std::vector< float > getRotation() const
Returns the rotation.
bool setPosition(float x, float y, float z)
Sets the position.
float getPositionY() const
Returns the position y.
bool setScale(float sx, float sy, float sz)
Sets the scale.
float getRotationRoll() const
Returns the rotation roll.
std::vector< float > getScale() const
Returns the scale.
int getChildCount() const
Returns the child count.
bool isVisible() const
True when visible.
float getScaleY() const
Returns the scale y.
std::string getParentId() const
Returns the parent id.
std::vector< float > getUp() const
Returns the up.
bool setRotation(float yaw, float pitch, float roll)
Sets the rotation.
std::vector< float > getRight() const
Returns the right.
float getPositionZ() const
Returns the position z.
std::vector< float > getWorldMatrix() const
Column-major 4x4 world matrix, 16 floats.
Per-node ECS identity (created lazily when a node needs script bindings or engine components)....
Definition SceneObject.h:25
static eve::Result< SceneObject * > createObject(const std::string &hostName, const std::string &nodeId, eve::SceneObjectId persistentId={})
Creates an ECS-owned companion object with an explicit ownership contract.
Declarative scene module (eve.Scene).
Definition Scene.h:51
void beginNode(const std::string &id, const std::string &name="")
Begins node.
Definition Scene.cpp:856
SceneNodeRef * getNodeRefByPathAt(const std::string &hostName, const std::string &path) const
Returns the node ref by path at.
Definition Scene.cpp:1186
bool linkPhysics3DAt(const std::string &hostName, const std::string &nodeId, physics::Body3D *b, const std::string &mode)
Link physics 3 d at.
SceneHost * resolveHost(const std::string &hostName) const
Host-qualified resolution: empty hostName → currently selected host.
Definition Scene.cpp:948
eve::Result< SceneHost * > findHostByOwner(uint32_t ownerId) const
Finds the host bound to an owner id with an explicit status.
Definition Scene.cpp:772
void setBuildSpace(const std::string &space)
Sets the build space.
Definition Scene.cpp:903
bool mountBuild()
Mounts build.
Definition Scene.cpp:909
std::vector< std::string > collectIdsFrom(const std::string &id)
Collect ids from.
void updateTransforms()
Propagate transforms (+ link sync) for all hosts (or current if selected).
Definition Scene.cpp:795
bool setNodeParentAt(const std::string &hostName, const std::string &childId, const std::string &parentId)
Reparent by id; empty parentId detaches. Cycle-safe.
std::vector< std::string > collectIds()
Collect ids.
bool linkCamera3DAt(const std::string &hostName, const std::string &nodeId, graphics::Camera3D *c)
Link camera 3 d at.
std::string serializeHostAt(const std::string &hostName) const
Serialize host at.
Definition Scene.cpp:831
std::vector< std::string > collectIdsByTagAt(const std::string &hostName, const std::string &tag) const
Collect ids by tag at.
void setBuildVisible(bool visible)
Sets the build visible.
Definition Scene.cpp:905
std::vector< float > getNodeRotationAt(const std::string &hostName, const std::string &nodeId) const
Returns the node rotation at.
std::string findIdByPath(const std::string &path)
Finds id by path.
bool rootEntity(const std::string &hostName, const std::string &nodeId, ssq::Object instance)
Root one script instance on a node (creates SceneObject lazily).
Definition Scene.cpp:998
std::string pickScreenAt(const std::string &hostName, graphics::Camera3D *cam, float screenX, float screenY, float viewW, float viewH) const
Screen-space picking through a Camera3D (camera.screenToRay).
eve::Result< void > scheduleDetachEntityAt(const std::string &hostName, const std::string &nodeId, ssq::Object instance)
Queue a script instance for detach at the end of the current/next updateScripts.
Definition Scene.cpp:1094
void beginBuild()
Begins build.
Definition Scene.cpp:843
~Scene() override
Tears down script bindings and delayed-detach state.
Definition Scene.cpp:43
bool setNodeQuaternionAt(const std::string &hostName, const std::string &nodeId, float qx, float qy, float qz, float qw)
Sets the node quaternion at.
eve::Result< SceneHost * > mountAs(const std::string &name, NodeDesc root)
Creates/replaces a named host from a NodeDesc tree and selects it.
Definition Scene.cpp:690
bool getNodeVisibleAt(const std::string &hostName, const std::string &nodeId) const
Returns the node visible at.
std::vector< float > worldToLocalAt(const std::string &hostName, const std::string &nodeId, float x, float y, float z) const
World to local at.
void setBuildRotation(float yaw, float pitch=0.f, float roll=0.f)
Sets the build rotation.
Definition Scene.cpp:889
eve::Result< SceneHost * > mount(NodeDesc root)
Mounts the tree as the default host and selects it.
Definition Scene.cpp:709
void updateScripts(float dt)
updateTransformsAll() + call update(dt) on every rooted instance.
Definition Scene.cpp:1062
std::vector< float > getNodePositionAt(const std::string &hostName, const std::string &nodeId) const
Returns the node position at.
std::vector< float > getNodeWorldPositionAt(const std::string &hostName, const std::string &nodeId) const
Returns the node world position at.
int getNodeLayerAt(const std::string &hostName, const std::string &nodeId) const
Returns the node layer at.
bool linkPhysics2DAt(const std::string &hostName, const std::string &nodeId, physics::Body *b, const std::string &mode)
Link physics 2 d at.
bool removeNodeAt(const std::string &hostName, const std::string &nodeId)
Detach node from its parent (arena node stays; rebuild to delete).
void flushDelayedDetaches()
Drain the delayed-detach queue (called automatically by updateScripts).
Definition Scene.cpp:1147
SceneNodeRef * getNodeRefAt(const std::string &hostName, const std::string &nodeId) const
Returns the node ref at.
Definition Scene.cpp:1178
void end()
Ends .
Definition Scene.cpp:866
std::vector< std::string > collectChildIds(const std::string &parentId)
Collect child ids.
void setHostVisible(bool visible)
Shows/hides every host (or the selected one when a host is selected).
Definition Scene.cpp:787
bool unlinkNodeAt(const std::string &hostName, const std::string &nodeId)
Remove every link on the node.
bool setNodePosition(const std::string &id, float x, float y, float z)
Script-friendly TRS setters on the current host; each marks the transform dirty.
std::vector< std::string > collectIdsVisible(bool visible)
Collect ids visible.
bool linkAudio3DAt(const std::string &hostName, const std::string &nodeId, audio::Source *s)
Link audio 3 d at.
bool setNodeLookAtAt(const std::string &hostName, const std::string &nodeId, float tx, float ty, float tz)
Orient node so its local +Z axis points at (tx,ty,tz).
std::string currentHostName() const
Current host name.
Definition Scene.cpp:1174
std::vector< float > getNodeWorldRotationAt(const std::string &hostName, const std::string &nodeId) const
Returns the node world rotation at.
bool hasNode(const std::string &id)
True when node.
void bindOwner(uint32_t ownerId)
Binds the selected host to a UI/scene owner id.
Definition Scene.cpp:782
std::string getNodePath(const std::string &id)
Returns the node path.
std::string findIdByName(const std::string &name)
Finds id by name.
int getChildCount(const std::string &id)
Returns the child count.
bool removeNodeTagAt(const std::string &hostName, const std::string &nodeId, const std::string &tag)
Removes node tag at.
bool linkRenderable3DAt(const std::string &hostName, const std::string &nodeId, graphics::Renderable3D *r)
Link renderable 3 d at.
eve::Result< SceneHost * > remount(NodeDesc root)
Replaces the selected host's tree.
Definition Scene.cpp:711
void setBuildPosition(float x, float y, float z=0.f)
Sets the build position.
Definition Scene.cpp:882
bool mountBuildAs(const std::string &name)
Mounts build as.
Definition Scene.cpp:918
bool isAncestor(const std::string &ancestorId, const std::string &nodeId)
True when ancestor.
void beginGroup(const std::string &id="")
Begins group.
Definition Scene.cpp:861
bool setNodeScale(const std::string &id, float sx, float sy, float sz)
Sets the local scale of a node.
std::string getParentId(const std::string &id)
Returns the parent id.
std::vector< std::string > collectIdsByName(const std::string &name)
Collect ids by name.
void setHostLayer(int layer)
Sets the render layer of every host (or the selected one).
Definition Scene.cpp:791
bool unlinkNodeKindAt(const std::string &hostName, const std::string &nodeId, const std::string &kind)
Remove links of one kind ("renderable2d"|"renderable3d"|"physics2d"|...).
std::vector< float > getNodeScaleAt(const std::string &hostName, const std::string &nodeId) const
Returns the node scale at.
std::string nodeIdFromSpatialIdAt(const std::string &hostName, int index) const
Map a spatial-index id (arena index) back to a node id.
eve::Result< SceneHost * > remountAs(const std::string &name, NodeDesc root)
Creates/replaces a named host through the canonical mount path.
Definition Scene.cpp:752
std::vector< std::string > getNodeTagsAt(const std::string &hostName, const std::string &nodeId) const
Returns the node tags at.
void updateTransformsAll()
Propagate transforms for every host regardless of selection.
Definition Scene.cpp:800
int linkCountAt(const std::string &hostName, const std::string &nodeId)
Link count at.
bool hasNodeBoundsAt(const std::string &hostName, const std::string &nodeId) const
True when node bounds at.
bool deserializeHostAt(const std::string &hostName, const std::string &json)
Deserialize host at.
Definition Scene.cpp:836
std::vector< std::string > walkBreadthFirstIds()
Walk breadth first ids.
bool unrootEntityAt(const std::string &hostName, const std::string &nodeId, int index)
Remove a rooted script instance by its index in the binding list.
Definition Scene.cpp:1018
bool remountBuildAs(const std::string &name)
Remount build as.
Definition Scene.cpp:927
bool linkRenderable3D(const std::string &nodeId, graphics::Renderable3D *r)
Links a 3D renderable to a node in the current host.
bool setNodeLayerAt(const std::string &hostName, const std::string &nodeId, int layer)
Sets the node layer at.
bool setNodeRotation(const std::string &id, float yaw, float pitch, float roll)
Sets the local rotation (yaw/pitch/roll in degrees) of a node.
bool unlinkNode(const std::string &nodeId)
Removes every link on a node in the current host.
bool syncSpatialIndexAt(const std::string &hostName, spatial::Octree *ot) const
Insert every bounded node's world AABB into an octree (id = arena index).
std::vector< float > getNodeQuaternionAt(const std::string &hostName, const std::string &nodeId) const
Returns the node quaternion at.
void addNode(const std::string &id, const std::string &name="")
Adds node.
Definition Scene.cpp:878
bool removeChildAt(const std::string &hostName, const std::string &parentId, const std::string &childId)
Removes child at.
eve::Result< SceneHost * > findHost(const std::string &name) const
Finds a host by name with an explicit not-found status.
Definition Scene.cpp:763
bool setNodeBoundsAt(const std::string &hostName, const std::string &nodeId, float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Sets the node bounds at.
bool setNodeVisible(const std::string &id, bool visible)
Shows/hides a node.
std::string getRootId()
Returns the root id.
bool addChildAt(const std::string &hostName, const std::string &parentId, const std::string &childId)
Adds child at.
std::string getChildIdAt(const std::string &parentId, int childOrdinal)
Returns the child id at.
bool linkRenderable2DAt(const std::string &hostName, const std::string &nodeId, graphics::Renderable2D *r)
Link renderable 2 d at.
Scene()
Scene.
Definition Scene.cpp:38
std::vector< float > localToWorldAt(const std::string &hostName, const std::string &nodeId, float x, float y, float z) const
Local to world at.
std::vector< float > getNodeWorldScaleAt(const std::string &hostName, const std::string &nodeId) const
Returns the node world scale at.
bool select(const std::string &name)
Selects a named host; false when it does not exist.
Definition Scene.cpp:756
bool isDescendant(const std::string &nodeId, const std::string &ancestorId)
True when descendant.
bool hasNodeTagAt(const std::string &hostName, const std::string &nodeId, const std::string &tag) const
True when node tag at.
int getNodeCount()
Returns the node count.
std::vector< std::string > walkDepthFirstIds()
DFS order of ids under root (same as collectIds). Kept for script naming clarity.
std::vector< std::string > collectFrustumIdsAt(const std::string &hostName, graphics::Camera3D *cam, float viewW, float viewH) const
Ids of nodes whose world AABB intersects the camera frustum.
void setBuildScale(float sx, float sy, float sz=1.f)
Sets the build scale.
Definition Scene.cpp:896
eve::Result< SceneHost * > remountReconcile(NodeDesc root)
Reconciles the selected host, reporting whether replacement failed.
Definition Scene.cpp:731
std::vector< float > getNodeBoundsAt(const std::string &hostName, const std::string &nodeId) const
Returns the node bounds at.
std::string pickRayAt(const std::string &hostName, float ox, float oy, float oz, float dx, float dy, float dz) const
Nearest node id hit by a world ray (nodes with bounds), or "".
bool addNodeTagAt(const std::string &hostName, const std::string &nodeId, const std::string &tag)
Adds node tag at.
bool linkRenderable2D(const std::string &nodeId, graphics::Renderable2D *r)
Links a 2D renderable to a node in the current host.
int forEachEntity(const std::string &hostName, const std::string &nodeId, ssq::Object cb)
Call cb(instance, index) for every rooted instance; returns count.
Definition Scene.cpp:1039
static void updateHost(SceneHost *host)
Updates host.
static void updateAll()
Updates all.
eve::StatusCode Status
eve::Result< T > Result
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
void registerSceneCapabilities()
Registers scene capabilities.
void registerSceneArtifactProvider()
Register the scene provider in the common capability registry.
void exposeFollowPlayerBindings(ssq::Table &table)
Register FollowPlayer value types and evaluator with the root script table.
void exposePcgBuildConfigBindings(ssq::Table &t)
Register Pcg build-config bindings.
void exposePcgGrowthBindings(ssq::Table &t)
Register Pcg Growth bindings.
Definition PcgGrowth.cpp:15
void exposeLocationProfileBindings(ssq::Table &table)
Register Pcg location profile value types and operations.
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
StatusCode
Stable outcome category for an operation.
Definition Status.h:27
void registerPostEcsHook(PostEcsHook fn)
Registers post ecs hook.
Definition ECS.cpp:865
SettlementPipeline::Stage fn
bool enabled
Scene-owned CPU registry provider for generated artifacts.
Declarative scene-node description (build once / on dirty → flatten into SceneHost::Tree)....
Definition NodeDesc.h:19
std::vector< NodeDesc > children
Definition NodeDesc.h:45
std::string space
"2d" or "3d" (string enum per module convention).
Definition NodeDesc.h:33
Retained scene node (arena). Conceptual GameObject; isomorphic to eve::ui::UINode.
Definition SceneHost.h:46