17 void GraphNode::dirtifyLocal()
27 void GraphNode::dirtifyWorld()
30 unfreezeParentToRoot();
32 dirtifyWorldInternal();
35 void GraphNode::dirtifyWorldInternal()
40 for (
auto* child : _children) {
41 if (!child->_dirtyWorld) {
42 child->dirtifyWorldInternal();
51 void GraphNode::unfreezeParentToRoot()
66 if (_parent ==
nullptr) {
69 _localRotation = _parent->rotation().invert() *
rotation;
87 if (!_dirtyLocal && !_dirtyWorld) {
88 return _worldTransform;
92 _parent->worldTransform();
96 return _worldTransform;
101 if (_worldScaleSign == 0) {
103 const float m00 = wt.getElement(0, 0);
104 const float m01 = wt.getElement(0, 1);
105 const float m02 = wt.getElement(0, 2);
106 const float m10 = wt.getElement(1, 0);
107 const float m11 = wt.getElement(1, 1);
108 const float m12 = wt.getElement(1, 2);
109 const float m20 = wt.getElement(2, 0);
110 const float m21 = wt.getElement(2, 1);
111 const float m22 = wt.getElement(2, 2);
112 const float det3 = m00 * (m11 * m22 - m12 * m21) -
113 m01 * (m10 * m22 - m12 * m20) +
114 m02 * (m10 * m21 - m11 * m20);
115 _worldScaleSign = det3 < 0.0f ? -1 : 1;
117 return static_cast<float>(_worldScaleSign);
120 void GraphNode::sync()
123 _localTransform =
Matrix4::trs(_localPosition, _localRotation, _localScale);
128 if (_parent ==
nullptr) {
129 _worldTransform = _localTransform;
131 if (_scaleCompensation) {
133 Vector3 parentWorldScale;
135 Vector3 scale = _localScale;
137 if (
auto* parentToUseScaleFrom = _parent) {
138 while (parentToUseScaleFrom && parentToUseScaleFrom->_scaleCompensation) {
139 parentToUseScaleFrom = parentToUseScaleFrom->_parent;
141 if (parentToUseScaleFrom) {
142 parentToUseScaleFrom = parentToUseScaleFrom->_parent;
143 if (parentToUseScaleFrom) {
144 parentWorldScale = parentToUseScaleFrom->worldTransform().getScale();
145 scale = parentWorldScale * _localScale;
151 const Quaternion scaleCompensateRot = scaleCompensateRot2 * _localRotation;
153 Vector3 scaleCompensatePos;
154 Matrix4 tmatrix = _parent->worldTransform();
155 if (_parent->_scaleCompensation) {
156 Vector3 scaleCompensateScaleForParent = parentWorldScale * _parent->localScale();
157 scaleCompensatePos = _parent->worldTransform().getTranslation();
158 tmatrix =
Matrix4::trs(scaleCompensatePos, scaleCompensateRot2, scaleCompensateScaleForParent);
160 scaleCompensatePos = tmatrix.transformPoint(_localPosition);
162 _worldTransform =
Matrix4::trs(scaleCompensatePos, scaleCompensateRot, scale);
164 _worldTransform = _parent->worldTransform().mulAffine(_localTransform);
186 _localPosition = _localPosition + offset;
196 _localRotation = _localRotation *
rotation;
205 prepareInsertChild(node);
206 _children.push_back(node);
225 _parent->removeChild(
this);
231 auto it = std::find(_children.begin(), _children.end(), child);
232 if (it == _children.end()) {
237 child->_parent =
nullptr;
238 child->fireOnHierarchy(
"remove",
"removehierarchy",
this);
239 fire(
"childremove", child);
242 void GraphNode::fireOnHierarchy(
const std::string& name,
const std::string& nameHierarchy,
GraphNode* parent)
245 for (
auto* child : _children) {
246 child->fireOnHierarchy(nameHierarchy, nameHierarchy,
parent);
250 void GraphNode::prepareInsertChild(GraphNode* node)
254 assert(node !=
this && (std::string(
"GraphNode ") + node->name() +
" cannot be a child of itself").c_str());
255 assert(!
isDescendantOf(node) && (std::string(
"GraphNode ") + node->name() +
" cannot add an ancestor as a child").c_str());
258 void GraphNode::onInsertChild(
GraphNode* node)
260 node->_parent =
this;
265 bool enabledInHierarchy =
enabled() && node->_enabled;
266 if (node->_enabledInHierarchy != enabledInHierarchy) {
267 node->_enabledInHierarchy = enabledInHierarchy;
268 node->notifyHierarchyStateChanged(node, enabledInHierarchy);
271 node->updateGraphDepth();
272 node->dirtifyWorld();
275 node->unfreezeParentToRoot();
278 node->fireOnHierarchy(
"insert",
"inserthierarchy",
this);
279 fire(
"childinsert", node);
286 for (
auto* child : node->_children) {
287 if (child->_enabled) {
296 if (_enabled != value) {
301 if ((value && _parent && _parent->enabled()) || !value) {
311 unfreezeParentToRoot();
315 void GraphNode::updateGraphDepth()
317 _graphDepth = _parent ? _parent->_graphDepth + 1 : 0;
319 for (
auto* child : _children) {
320 child->updateGraphDepth();
326 _localPosition =
Vector3(x, y, z);
365 if (_parent ==
nullptr) {
383 for (
auto* child : _children) {
398 std::vector<GraphNode*> results;
400 if (predicate(
this)) {
401 results.push_back(
this);
404 for (
auto* child : _children) {
408 auto childResults = child->find(predicate);
409 results.insert(results.end(), childResults.begin(), childResults.end());
EventHandler * fire(const std::string &name, Args &&... args)
Hierarchical scene graph node with local/world transforms and parent-child relationships.
GraphNode * findByName(const std::string &name)
bool isDescendantOf(const GraphNode *node) const
void setPosition(float x, float y, float z)
GraphNode(const std::string &name="Untitled")
void notifyHierarchyStateChanged(GraphNode *node, bool enabled)
void setLocalEulerAngles(float x, float y, float z)
void setLocalPosition(float x, float y, float z)
void addChild(GraphNode *node)
virtual void onHierarchyStateChanged(bool enabled)
std::vector< GraphNode * > find(const std::function< bool(GraphNode *)> &predicate)
Find all descendants (and self) matching a predicate.
void translateLocal(float x, float y, float z)
void setRotation(const Quaternion &rotation)
GraphNode * parent() const
void removeChild(GraphNode *child)
const Matrix4 & worldTransform()
const std::string & name() const
void setEnabled(bool value)
void setLocalRotation(const Quaternion &rotation)
void rotateLocal(float x, float y, float z)
void setLocalScale(float x, float y, float z)
4x4 column-major transformation matrix with SIMD acceleration.
Vector3 transformPoint(const Vector3 &v) const
Vector3 getTranslation() const
static Matrix4 trs(const Vector3 &t, const Quaternion &r, const Vector3 &s)
Unit quaternion for rotation representation with SIMD-accelerated slerp and multiply.
static Quaternion fromEulerAngles(float ax, float ay, float az)
static Quaternion fromMatrix4(const Matrix4 &m)
3D vector for positions, directions, and normals with multi-backend SIMD acceleration.