Coverage Report

Created: 2025-12-14 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreSceneNode.cpp
Line
Count
Source
1
/*
2
-----------------------------------------------------------------------------
3
This source file is part of OGRE
4
    (Object-oriented Graphics Rendering Engine)
5
For the latest info, see http://www.ogre3d.org/
6
7
Copyright (c) 2000-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
#include "OgreStableHeaders.h"
29
30
namespace Ogre {
31
    //-----------------------------------------------------------------------
32
    SceneNode::SceneNode(SceneManager* creator, const String& name)
33
0
        : Node(name)
34
0
        , mCreator(creator)
35
0
        , mAutoTrackTarget(0)
36
0
        , mGlobalIndex(-1)
37
0
        , mYawFixed(false)
38
0
        , mIsInSceneGraph(false)
39
0
        , mShowBoundingBox(false)
40
0
    {
41
0
        needUpdate();
42
0
    }
43
    //-----------------------------------------------------------------------
44
    SceneNode::~SceneNode()
45
0
    {
46
        // Detach all objects, do this manually to avoid needUpdate() call 
47
        // which can fail because of deleted items
48
0
        for (auto & itr : mObjectsByName)
49
0
        {
50
0
            itr->_notifyAttached((SceneNode*)0);
51
0
        }
52
0
        mObjectsByName.clear();
53
0
    }
54
    //-----------------------------------------------------------------------
55
    void SceneNode::_update(bool updateChildren, bool parentHasChanged)
56
0
    {
57
0
        Node::_update(updateChildren, parentHasChanged);
58
0
        _updateBounds();
59
0
    }
60
    //-----------------------------------------------------------------------
61
    void SceneNode::setParent(Node* parent)
62
0
    {
63
0
        Node::setParent(parent);
64
65
0
        if (parent)
66
0
        {
67
0
            SceneNode* sceneParent = static_cast<SceneNode*>(parent);
68
0
            setInSceneGraph(sceneParent->isInSceneGraph());
69
0
        }
70
0
        else
71
0
        {
72
0
            setInSceneGraph(false);
73
0
        }
74
0
    }
75
    //-----------------------------------------------------------------------
76
    void SceneNode::setInSceneGraph(bool inGraph)
77
0
    {
78
0
        if (inGraph != mIsInSceneGraph)
79
0
        {
80
0
            mIsInSceneGraph = inGraph;
81
            // Tell children
82
0
            for (auto child : getChildren())
83
0
            {
84
0
                SceneNode* sceneChild = static_cast<SceneNode*>(child);
85
0
                sceneChild->setInSceneGraph(inGraph);
86
0
            }
87
0
        }
88
0
    }
89
    //-----------------------------------------------------------------------
90
    struct MovableObjectNameExists {
91
        const String& name;
92
0
        bool operator()(const MovableObject* mo) {
93
0
            return mo->getName() == name;
94
0
        }
95
    };
96
    void SceneNode::attachObject(MovableObject* obj)
97
0
    {
98
0
        OgreAssert(!obj->isAttached(), "Object already attached to a SceneNode or a Bone");
99
100
0
        obj->_notifyAttached(this);
101
102
        // Also add to name index
103
0
        MovableObjectNameExists pred = {obj->getName()};
104
0
        ObjectMap::iterator it = std::find_if(mObjectsByName.begin(), mObjectsByName.end(), pred);
105
0
        if (it != mObjectsByName.end())
106
0
            OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM,
107
0
                        "An object named '" + obj->getName() + "' already attached to this SceneNode");
108
0
        mObjectsByName.push_back(obj);
109
110
        // Make sure bounds get updated (must go right to the top)
111
0
        needUpdate();
112
0
    }
113
    //-----------------------------------------------------------------------
114
    MovableObject* SceneNode::getAttachedObject(const String& name) const
115
0
    {
116
        // Look up 
117
0
        MovableObjectNameExists pred = {name};
118
0
        auto i = std::find_if(mObjectsByName.begin(), mObjectsByName.end(), pred);
119
120
0
        if (i == mObjectsByName.end())
121
0
        {
122
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Attached object " + 
123
0
                name + " not found.", "SceneNode::getAttachedObject");
124
0
        }
125
126
0
        return *i;
127
0
    }
128
    //-----------------------------------------------------------------------
129
    MovableObject* SceneNode::detachObject(unsigned short index)
130
0
    {
131
0
        OgreAssert(index < mObjectsByName.size(), "out of bounds");
132
0
        ObjectMap::iterator i = mObjectsByName.begin();
133
0
        i += index;
134
135
0
        MovableObject* ret = *i;
136
0
        std::swap(*i, mObjectsByName.back());
137
0
        mObjectsByName.pop_back();
138
139
0
        ret->_notifyAttached((SceneNode*)0);
140
141
        // Make sure bounds get updated (must go right to the top)
142
0
        needUpdate();
143
144
0
        return ret;
145
0
    }
146
    //-----------------------------------------------------------------------
147
    MovableObject* SceneNode::detachObject(const String& name)
148
0
    {
149
0
        MovableObjectNameExists pred = {name};
150
0
        ObjectMap::iterator it = std::find_if(mObjectsByName.begin(), mObjectsByName.end(), pred);
151
152
0
        if (it == mObjectsByName.end())
153
0
        {
154
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Object " + name + " is not attached "
155
0
                "to this node.", "SceneNode::detachObject");
156
0
        }
157
158
0
        MovableObject* ret = *it;
159
0
        std::swap(*it, mObjectsByName.back());
160
0
        mObjectsByName.pop_back();
161
162
0
        ret->_notifyAttached((SceneNode*)0);
163
        // Make sure bounds get updated (must go right to the top)
164
0
        needUpdate();
165
        
166
0
        return ret;
167
168
0
    }
169
    //-----------------------------------------------------------------------
170
    void SceneNode::detachObject(MovableObject* obj)
171
0
    {
172
0
        auto it = std::find(mObjectsByName.begin(), mObjectsByName.end(), obj);
173
0
        OgreAssert(it != mObjectsByName.end(), "Object is not attached to this node");
174
0
        std::swap(*it, mObjectsByName.back());
175
0
        mObjectsByName.pop_back();
176
0
        obj->_notifyAttached((SceneNode*)0);
177
178
        // Make sure bounds get updated (must go right to the top)
179
0
        needUpdate();
180
0
    }
181
    //-----------------------------------------------------------------------
182
    void SceneNode::detachAllObjects(void)
183
0
    {
184
0
        for (auto & itr : mObjectsByName)
185
0
        {
186
0
            itr->_notifyAttached((SceneNode*)0);
187
0
        }
188
0
        mObjectsByName.clear();
189
        // Make sure bounds get updated (must go right to the top)
190
0
        needUpdate();
191
0
    }
192
    //-----------------------------------------------------------------------
193
    void SceneNode::destroyAllObjects(void)
194
0
    {
195
0
        while (!getAttachedObjects().empty()) {
196
0
            auto obj = getAttachedObjects().front();
197
0
            getCreator()->destroyMovableObject(obj);
198
0
        }
199
0
        needUpdate();
200
0
    }
201
    //-----------------------------------------------------------------------
202
    void SceneNode::_updateBounds(void)
203
0
    {
204
        // Reset bounds first
205
0
        mWorldAABB.setNull();
206
207
        // Update bounds from own attached objects
208
0
        for (auto *o : mObjectsByName)
209
0
        {
210
            // Merge world bounds of each object
211
0
            mWorldAABB.merge(o->getWorldBoundingBox(true));
212
0
        }
213
214
        // Merge with children
215
0
        for (auto child : getChildren())
216
0
        {
217
0
            SceneNode* sceneChild = static_cast<SceneNode*>(child);
218
0
            mWorldAABB.merge(sceneChild->mWorldAABB);
219
0
        }
220
221
0
    }
222
    //-----------------------------------------------------------------------
223
    void SceneNode::_findVisibleObjects(Camera* cam, RenderQueue* queue, 
224
        VisibleObjectsBoundsInfo* visibleBounds, bool includeChildren, 
225
        bool displayNodes, bool onlyShadowCasters)
226
0
    {
227
        // Check self visible
228
0
        if (!cam->isVisible(mWorldAABB))
229
0
            return;
230
231
        // Add all entities
232
0
        for (auto *o : mObjectsByName)
233
0
        {
234
0
            queue->processVisibleObject(o, cam, onlyShadowCasters, visibleBounds);
235
0
        }
236
237
0
        if (includeChildren)
238
0
        {
239
0
            for (auto child : getChildren())
240
0
            {
241
0
                SceneNode* sceneChild = static_cast<SceneNode*>(child);
242
0
                sceneChild->_findVisibleObjects(cam, queue, visibleBounds, includeChildren, 
243
0
                    displayNodes, onlyShadowCasters);
244
0
            }
245
0
        }
246
247
0
        if (mCreator && mCreator->getDebugDrawer())
248
0
        {
249
0
            mCreator->getDebugDrawer()->drawSceneNode(this);
250
0
        }
251
0
    }
252
253
0
    SceneNode::ObjectIterator SceneNode::getAttachedObjectIterator(void) {
254
0
        return ObjectIterator(mObjectsByName.begin(), mObjectsByName.end());
255
0
    }
256
0
    SceneNode::ConstObjectIterator SceneNode::getAttachedObjectIterator(void) const {
257
0
        return ConstObjectIterator(mObjectsByName.begin(), mObjectsByName.end());
258
0
    }
259
260
    //-----------------------------------------------------------------------
261
    void SceneNode::updateFromParentImpl(void) const
262
0
    {
263
0
        Node::updateFromParentImpl();
264
265
        // Notify objects that it has been moved
266
0
        for (auto o : mObjectsByName)
267
0
        {
268
0
            o->_notifyMoved();
269
0
        }
270
0
    }
271
    //-----------------------------------------------------------------------
272
    Node* SceneNode::createChildImpl(void)
273
0
    {
274
0
        assert(mCreator);
275
0
        return mCreator->createSceneNode();
276
0
    }
277
    //-----------------------------------------------------------------------
278
    Node* SceneNode::createChildImpl(const String& name)
279
0
    {
280
0
        assert(mCreator);
281
0
        return mCreator->createSceneNode(name);
282
0
    }
283
    //-----------------------------------------------------------------------
284
    void SceneNode::removeAndDestroyChild(const String& name)
285
0
    {
286
0
        SceneNode* pChild = static_cast<SceneNode*>(removeChild(name));
287
0
        pChild->removeAndDestroyAllChildren();
288
289
0
        pChild->getCreator()->destroySceneNode(name);
290
291
0
    }
292
    //-----------------------------------------------------------------------
293
    void SceneNode::removeAndDestroyChild(unsigned short index)
294
0
    {
295
0
        SceneNode* pChild = static_cast<SceneNode*>(removeChild(index));
296
0
        pChild->removeAndDestroyAllChildren();
297
298
0
        pChild->getCreator()->destroySceneNode(pChild);
299
0
    }
300
    //-----------------------------------------------------------------------
301
    void SceneNode::removeAndDestroyChild(SceneNode* child)
302
0
    {
303
0
        auto it = std::find(getChildren().begin(), getChildren().end(), child);
304
0
        OgreAssert(it != getChildren().end(), "Not a child of this SceneNode");
305
0
        removeAndDestroyChild(it - getChildren().begin());
306
0
    }
307
    //-----------------------------------------------------------------------
308
    void SceneNode::removeAndDestroyAllChildren(void)
309
0
    {
310
        // do not store iterators (invalidated by
311
        // SceneManager::destroySceneNode because it causes removal from parent)
312
0
        while(!getChildren().empty()) {
313
0
            SceneNode* sn = static_cast<SceneNode*>(getChildren().front());
314
0
            sn->removeAndDestroyAllChildren();
315
0
            sn->getCreator()->destroySceneNode(sn);
316
0
        }
317
318
0
        mChildren.clear();
319
0
        needUpdate();
320
0
    }
321
    //-----------------------------------------------------------------------
322
0
    void SceneNode::destroyChildAndObjects(const String& name) {
323
0
        SceneNode* pChild = static_cast<SceneNode*>(getChild(name));
324
0
        pChild->destroyAllChildrenAndObjects();
325
326
0
        removeChild(name);
327
0
        pChild->getCreator()->destroySceneNode(name);
328
329
0
    }
330
331
0
    void SceneNode::destroyChildAndObjects(unsigned short index) {
332
0
        SceneNode* pChild = static_cast<SceneNode*>(removeChild(index));
333
0
        pChild->destroyAllChildrenAndObjects();
334
335
0
        pChild->getCreator()->destroySceneNode(pChild);
336
0
    }
337
338
    void SceneNode::destroyChildAndObjects(SceneNode * child)
339
0
    {
340
0
        auto it = std::find(getChildren().begin(), getChildren().end(), child);
341
0
        OgreAssert(it != getChildren().end(), "Not a child of this SceneNode");
342
0
        destroyChildAndObjects(it - getChildren().begin());
343
0
    }
344
345
    void SceneNode::destroyAllChildrenAndObjects()
346
0
    {
347
        //remove objects directly attached to this node
348
0
        destroyAllObjects();
349
350
        //go over children
351
0
        while(!getChildren().empty()) {
352
0
            SceneNode* child = static_cast<SceneNode*>(getChildren().front());
353
            //recurse
354
0
            child->destroyAllChildrenAndObjects();
355
356
            //destroy child
357
0
            child->getCreator()->destroySceneNode(child);
358
0
        }
359
0
        mChildren.clear();
360
0
        needUpdate();
361
0
    }
362
    //-----------------------------------------------------------------------
363
    void SceneNode::loadChildren(const String& filename)
364
0
    {
365
0
        String baseName, strExt;
366
0
        StringUtil::splitBaseFilename(filename, baseName, strExt);
367
0
        auto codec = Codec::getCodec(strExt);
368
0
        if (!codec)
369
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "No codec found to load " + filename);
370
371
0
        auto stream = Root::openFileStream(
372
0
            filename, ResourceGroupManager::getSingleton().getWorldResourceGroupName());
373
0
        codec->decode(stream, this);
374
0
    }
375
    void SceneNode::saveChildren(const String& filename)
376
0
    {
377
0
        String baseName, strExt;
378
0
        StringUtil::splitBaseFilename(filename, baseName, strExt);
379
0
        auto codec = Codec::getCodec(strExt);
380
0
        codec->encodeToFile(this, filename);
381
0
    }
382
    //-----------------------------------------------------------------------
383
    SceneNode* SceneNode::createChildSceneNode(const Vector3& inTranslate, 
384
        const Quaternion& inRotate)
385
0
    {
386
0
        return static_cast<SceneNode*>(this->createChild(inTranslate, inRotate));
387
0
    }
388
    //-----------------------------------------------------------------------
389
    SceneNode* SceneNode::createChildSceneNode(const String& name, const Vector3& inTranslate, 
390
        const Quaternion& inRotate)
391
0
    {
392
0
        return static_cast<SceneNode*>(this->createChild(name, inTranslate, inRotate));
393
0
    }
394
    //-----------------------------------------------------------------------
395
    void SceneNode::findLights(LightList& destList, Real radius, uint32 lightMask) const
396
0
    {
397
        // No any optimisation here, hope inherits more smart for that.
398
        //
399
        // If a scene node is static and lights have moved, light list won't change
400
        // can't use a simple global boolean flag since this is only called for
401
        // visible nodes, so temporarily visible nodes will not be updated
402
        // Since this is only called for visible nodes, skip the check for now
403
        //
404
0
        if (mCreator)
405
0
        {
406
            // Use SceneManager to calculate
407
0
            mCreator->_populateLightList(this, radius, destList, lightMask);
408
0
        }
409
0
        else
410
0
        {
411
0
            destList.clear();
412
0
        }
413
0
    }
414
    //-----------------------------------------------------------------------
415
    void SceneNode::setAutoTracking(bool enabled, SceneNode* const target, 
416
        const Vector3& localDirectionVector,
417
        const Vector3& offset)
418
0
    {
419
0
        if (enabled)
420
0
        {
421
0
            mAutoTrackTarget = target;
422
0
            mAutoTrackOffset = offset;
423
0
            mAutoTrackLocalDirection = localDirectionVector;
424
0
        }
425
0
        else
426
0
        {
427
0
            mAutoTrackTarget = 0;
428
0
        }
429
0
        if (mCreator)
430
0
            mCreator->_notifyAutotrackingSceneNode(this, enabled);
431
0
    }
432
    //-----------------------------------------------------------------------
433
    void SceneNode::setFixedYawAxis(bool useFixed, const Vector3& fixedAxis)
434
0
    {
435
0
        mYawFixed = useFixed;
436
0
        mYawFixedAxis = fixedAxis;
437
0
    }
438
439
    //-----------------------------------------------------------------------
440
    void SceneNode::yaw(const Radian& angle, TransformSpace relativeTo)
441
0
    {
442
0
        if (mYawFixed)
443
0
        {
444
0
            rotate(mYawFixedAxis, angle, relativeTo);
445
0
        }
446
0
        else
447
0
        {
448
0
            rotate(Vector3::UNIT_Y, angle, relativeTo);
449
0
        }
450
451
0
    }
452
    //-----------------------------------------------------------------------
453
    void SceneNode::setDirection(Real x, Real y, Real z, TransformSpace relativeTo, 
454
        const Vector3& localDirectionVector)
455
0
    {
456
0
        setDirection(Vector3(x,y,z), relativeTo, localDirectionVector);
457
0
    }
458
459
    //-----------------------------------------------------------------------
460
    void SceneNode::setDirection(const Vector3& vec, TransformSpace relativeTo, 
461
        const Vector3& localDirectionVector)
462
0
    {
463
        // Do nothing if given a zero vector
464
0
        if (vec == Vector3::ZERO) return;
465
466
        // The direction we want the local direction point to
467
0
        Vector3 targetDir = vec.normalisedCopy();
468
469
        // Transform target direction to world space
470
0
        switch (relativeTo)
471
0
        {
472
0
        case TS_PARENT:
473
0
            if (getInheritOrientation())
474
0
            {
475
0
                if (getParent())
476
0
                {
477
0
                    targetDir = getParent()->_getDerivedOrientation() * targetDir;
478
0
                }
479
0
            }
480
0
            break;
481
0
        case TS_LOCAL:
482
0
            targetDir = _getDerivedOrientation() * targetDir;
483
0
            break;
484
0
        case TS_WORLD:
485
            // default orientation
486
0
            break;
487
0
        }
488
489
        // Calculate target orientation relative to world space
490
0
        Quaternion targetOrientation;
491
0
        if( mYawFixed )
492
0
        {
493
            // Calculate the quaternion for rotate local Z to target direction
494
0
            Vector3 yawAxis = mYawFixedAxis;
495
496
0
            if (getInheritOrientation() && getParent())
497
0
            {
498
0
                yawAxis = getParent()->_getDerivedOrientation() * yawAxis;
499
0
            }
500
501
0
            Quaternion unitZToTarget = Math::lookRotation(targetDir, yawAxis);
502
503
0
            if (localDirectionVector == Vector3::NEGATIVE_UNIT_Z)
504
0
            {
505
                // Specail case for avoid calculate 180 degree turn
506
0
                targetOrientation =
507
0
                    Quaternion(-unitZToTarget.y, -unitZToTarget.z, unitZToTarget.w, unitZToTarget.x);
508
0
            }
509
0
            else
510
0
            {
511
                // Calculate the quaternion for rotate local direction to target direction
512
0
                Quaternion localToUnitZ = localDirectionVector.getRotationTo(Vector3::UNIT_Z);
513
0
                targetOrientation = unitZToTarget * localToUnitZ;
514
0
            }
515
0
        }
516
0
        else
517
0
        {
518
0
            const Quaternion& currentOrient = _getDerivedOrientation();
519
520
            // Get current local direction relative to world space
521
0
            Vector3 currentDir = currentOrient * localDirectionVector;
522
523
0
            if ((currentDir+targetDir).squaredLength() < 0.00005f)
524
0
            {
525
                // Oops, a 180 degree turn (infinite possible rotation axes)
526
                // Default to yaw i.e. use current UP
527
0
                targetOrientation =
528
0
                    Quaternion(-currentOrient.y, -currentOrient.z, currentOrient.w, currentOrient.x);
529
0
            }
530
0
            else
531
0
            {
532
                // Derive shortest arc to new direction
533
0
                Quaternion rotQuat = currentDir.getRotationTo(targetDir);
534
0
                targetOrientation = rotQuat * currentOrient;
535
0
            }
536
0
        }
537
538
        // Set target orientation, transformed to parent space
539
0
        if (getParent() && getInheritOrientation())
540
0
            setOrientation(getParent()->_getDerivedOrientation().UnitInverse() * targetOrientation);
541
0
        else
542
0
            setOrientation(targetOrientation);
543
0
    }
544
    //-----------------------------------------------------------------------
545
    void SceneNode::lookAt( const Vector3& targetPoint, TransformSpace relativeTo, 
546
        const Vector3& localDirectionVector)
547
0
    {
548
        // Calculate ourself origin relative to the given transform space
549
0
        Vector3 origin;
550
0
        switch (relativeTo)
551
0
        {
552
0
        default:    // Just in case
553
0
        case TS_WORLD:
554
0
            origin = _getDerivedPosition();
555
0
            break;
556
0
        case TS_PARENT:
557
0
            origin = getPosition();
558
0
            break;
559
0
        case TS_LOCAL:
560
0
            origin = Vector3::ZERO;
561
0
            break;
562
0
        }
563
564
0
        setDirection(targetPoint - origin, relativeTo, localDirectionVector);
565
0
    }
566
    //-----------------------------------------------------------------------
567
    void SceneNode::_autoTrack(void)
568
0
    {
569
        // NB assumes that all scene nodes have been updated
570
0
        if (mAutoTrackTarget)
571
0
        {
572
0
            lookAt(mAutoTrackTarget->_getDerivedPosition() + mAutoTrackOffset, 
573
0
                TS_WORLD, mAutoTrackLocalDirection);
574
            // update self & children
575
0
            _update(true, true);
576
0
        }
577
0
    }
578
    //-----------------------------------------------------------------------
579
    SceneNode* SceneNode::getParentSceneNode(void) const
580
0
    {
581
0
        return static_cast<SceneNode*>(getParent());
582
0
    }
583
    //-----------------------------------------------------------------------
584
    void SceneNode::setVisible(bool visible, bool cascade) const
585
0
    {
586
0
        for (auto o : mObjectsByName)
587
0
        {
588
0
            o->setVisible(visible);
589
0
        }
590
591
0
        if (cascade)
592
0
        {
593
0
            for (auto c : getChildren())
594
0
            {
595
0
                static_cast<SceneNode*>(c)->setVisible(visible, cascade);
596
0
            }
597
0
        }
598
0
    }
599
    //-----------------------------------------------------------------------
600
    void SceneNode::setDebugDisplayEnabled(bool enabled, bool cascade) const
601
0
    {
602
0
        for (auto o : mObjectsByName)
603
0
        {
604
0
            o->setDebugDisplayEnabled(enabled);
605
0
        }
606
607
0
        if (cascade)
608
0
        {
609
0
            for (auto c : getChildren())
610
0
            {
611
0
                static_cast<SceneNode*>(c)->setDebugDisplayEnabled(enabled, cascade);
612
0
            }
613
0
        }
614
0
    }
615
    //-----------------------------------------------------------------------
616
    void SceneNode::flipVisibility(bool cascade) const
617
0
    {
618
0
        for (auto o : mObjectsByName)
619
0
        {
620
0
            o->setVisible(!o->getVisible());
621
0
        }
622
623
0
        if (cascade)
624
0
        {
625
0
            for (auto c : getChildren())
626
0
            {
627
0
                static_cast<SceneNode*>(c)->flipVisibility(cascade);
628
0
            }
629
0
        }
630
0
    }
631
}