7#include <glm/gtc/matrix_transform.hpp>
8#include <glm/gtc/quaternion.hpp>
13glm::mat4 localMatrix(
const SceneNode &
n) {
15 m = glm::translate(
m, glm::vec3(
n.x,
n.y,
n.z));
16 if (
n.space ==
"2d") {
17 m = glm::rotate(
m,
n.roll, glm::vec3(0.f, 0.f, 1.f));
19 m = glm::rotate(
m,
n.yaw, glm::vec3(0.f, 1.f, 0.f));
20 m = glm::rotate(
m,
n.pitch, glm::vec3(1.f, 0.f, 0.f));
21 m = glm::rotate(
m,
n.roll, glm::vec3(0.f, 0.f, 1.f));
23 m = glm::scale(
m, glm::vec3(
n.sx,
n.sy,
n.sz));
27void updateNode(SceneHost::Tree &tree,
int nodeIndex,
const glm::mat4 &parentWorld) {
28 SceneNode &
n = tree.nodes[size_t(nodeIndex)];
29 n.world = parentWorld * localMatrix(
n);
31 for (
int c =
n.firstChild;
c >= 0;
c = tree.nodes[size_t(
c)].nextSibling) {
32 updateNode(tree,
c,
n.world);
37void pullTargets(SceneHost *host) {
39 auto t = host->tree();
40 for (
size_t i = 0; i < t->nodes.size(); ++i) {
41 SceneNode &
n = t->nodes[i];
42 for (
auto &l :
n.links) {
43 if (l.syncMode != 1 || !l.target)
continue;
47 host->markSubtreeDirty(
int(i));
53void pushTarget(
const SceneNode &
n,
const SceneLink &l);
54void updateNodeIncremental(SceneHost::Tree &tree,
int nodeIndex,
55 const glm::mat4 &parentWorld,
bool force) {
56 SceneNode &
n = tree.nodes[size_t(nodeIndex)];
57 const bool need = force ||
n.subtreeDirty;
59 n.world = parentWorld * localMatrix(
n);
61 n.subtreeDirty =
false;
62 for (
auto &l :
n.links) {
63 if (l.syncMode == 0) pushTarget(
n, l);
66 for (
int c =
n.firstChild;
c >= 0;
c = tree.nodes[size_t(
c)].nextSibling) {
67 updateNodeIncremental(tree,
c,
n.world, need);
72void pushTarget(
const SceneNode &
n,
const SceneLink &l) {
73 if (!l.target)
return;
78void syncLinks(SceneHost *host) {
80 auto t = host->tree();
81 for (
auto &
n : t->nodes) {
82 for (
auto &l :
n.links) {
83 if (l.syncMode == 0) pushTarget(
n, l);
93bool linkTargetAlive(
const SceneLink &l) {
94 if (!l.target)
return false;
101void purgeDeadLinks(SceneHost::Tree &tree) {
102 for (
auto &
n : tree.nodes) {
103 for (
auto it =
n.links.begin(); it !=
n.links.end();) {
104 if (linkTargetAlive(*it)) {
107 it =
n.links.erase(it);
117 auto t = host->tree();
118 if (t->root < 0 || t->nodes.empty()) {
119 t->transformDirty =
false;
127 bool legacy = t->transformDirty;
129 for (
const auto &
n : t->nodes) {
130 if (
n.localDirty && !
n.subtreeDirty) {
137 updateNode(*t, t->root, glm::mat4(1.f));
139 for (
auto &
n : t->nodes) {
140 n.localDirty =
false;
141 n.subtreeDirty =
false;
143 t->transformDirty =
false;
148 bool anySubtree =
false;
149 for (
const auto &
n : t->nodes) {
150 if (
n.subtreeDirty) {
156 t->transformDirty =
false;
159 updateNodeIncremental(*t, t->root, glm::mat4(1.f),
false);
160 t->transformDirty =
false;
164 if (ecs::current()->getManager<SceneHost>() ==
nullptr)
return;
165 auto view = ecs::View<SceneHost, SceneHost::Meta, SceneHost::Tree>();
166 for (
auto it =
view.begin(); it !=
view.end(); ++it) {
167 auto [meta, tree] = *it;
169 if (!meta->entity)
continue;
ECS mount point for one scene graph (full scene or nested subtree root). Isomorphic to eve::ui::UIHos...
const LinkOps * linkOps(int kindId)
void(* pushWorld)(const SceneNode &node, void *target)
void(* pullWorld)(SceneNode &node, void *target)
bool(* alive)(const void *target)