Coverage Report

Created: 2026-01-09 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreMovableObject.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
#include "OgreTagPoint.h"
31
#include "OgreEntity.h"
32
#include "OgreLodListener.h"
33
34
namespace Ogre {
35
    //-----------------------------------------------------------------------
36
    //-----------------------------------------------------------------------
37
    uint32 MovableObject::msDefaultQueryFlags = 0xFFFFFFFF;
38
    uint32 MovableObject::msDefaultVisibilityFlags = 0xFFFFFFFF;
39
    //-----------------------------------------------------------------------
40
0
    MovableObject::MovableObject() : MovableObject(BLANKSTRING) {}
41
    //-----------------------------------------------------------------------
42
    MovableObject::MovableObject(const String& name)
43
0
        : mName(name)
44
0
        , mCreator(0)
45
0
        , mManager(0)
46
0
        , mParentNode(0)
47
0
        , mListener(0)
48
0
        , mParentIsTagPoint(false)
49
0
        , mVisible(true)
50
0
        , mDebugDisplay(false)
51
0
        , mBeyondFarDistance(false)
52
0
        , mCastShadows(true)
53
0
        , mRenderQueueIDSet(false)
54
0
        , mRenderQueuePrioritySet(false)
55
0
        , mRenderingDisabled(false)
56
0
        , mRenderQueueID(RENDER_QUEUE_MAIN)
57
0
        , mRenderQueuePriority(100)
58
0
        , mUpperDistance(0)
59
0
        , mSquaredUpperDistance(0)
60
0
        , mMinPixelSize(0)
61
0
        , mQueryFlags(msDefaultQueryFlags)
62
0
        , mVisibilityFlags(msDefaultVisibilityFlags)
63
0
        , mLightListUpdated(0)
64
0
        , mLightMask(0xFFFFFFFF)
65
0
    {
66
0
        if (Root::getSingletonPtr())
67
0
            mMinPixelSize = Root::getSingleton().getDefaultMinPixelSize();
68
0
    }
69
    //-----------------------------------------------------------------------
70
    MovableObject::~MovableObject()
71
0
    {
72
        // Call listener (note, only called if there's something to do)
73
0
        if (mListener)
74
0
        {
75
0
            mListener->objectDestroyed(this);
76
0
        }
77
78
0
        detachFromParent(); // this should never throw, if it does terminating is the thing to do
79
0
    }
80
    //-----------------------------------------------------------------------
81
    void MovableObject::_notifyAttached(Node* parent, bool isTagPoint)
82
0
    {
83
0
        assert(!mParentNode || !parent);
84
85
0
        bool different = (parent != mParentNode);
86
87
0
        mParentNode = parent;
88
0
        mParentIsTagPoint = isTagPoint;
89
90
        // Mark light list being dirty, simply decrease
91
        // counter by one for minimise overhead
92
0
        --mLightListUpdated;
93
94
        // Call listener (note, only called if there's something to do)
95
0
        if (mListener && different)
96
0
        {
97
0
            if (mParentNode)
98
0
                mListener->objectAttached(this);
99
0
            else
100
0
                mListener->objectDetached(this);
101
0
        }
102
0
    }
103
    //-----------------------------------------------------------------------
104
    SceneNode* MovableObject::getParentSceneNode(void) const
105
0
    {
106
0
        if (mParentIsTagPoint)
107
0
        {
108
0
            TagPoint* tp = static_cast<TagPoint*>(mParentNode);
109
0
            return tp->getParentEntity()->getParentSceneNode();
110
0
        }
111
0
        else
112
0
        {
113
0
            return static_cast<SceneNode*>(mParentNode);
114
0
        }
115
0
    }
116
    //---------------------------------------------------------------------
117
    void MovableObject::detachFromParent(void)
118
0
    {
119
0
        if (isAttached())
120
0
        {
121
0
            if (mParentIsTagPoint)
122
0
            {
123
0
                TagPoint* tp = static_cast<TagPoint*>(mParentNode);
124
0
                tp->getParentEntity()->detachObjectFromBone(this);
125
0
            }
126
0
            else
127
0
            {
128
0
                SceneNode* sn = static_cast<SceneNode*>(mParentNode);
129
0
                sn->detachObject(this);
130
0
            }
131
0
        }
132
0
    }
133
    //-----------------------------------------------------------------------
134
    bool MovableObject::isInScene(void) const
135
0
    {
136
0
        if (mParentNode != 0)
137
0
        {
138
0
            if (mParentIsTagPoint)
139
0
            {
140
0
                TagPoint* tp = static_cast<TagPoint*>(mParentNode);
141
0
                return tp->getParentEntity()->isInScene();
142
0
            }
143
0
            else
144
0
            {
145
0
                SceneNode* sn = static_cast<SceneNode*>(mParentNode);
146
0
                return sn->isInSceneGraph();
147
0
            }
148
0
        }
149
0
        else
150
0
        {
151
0
            return false;
152
0
        }
153
0
    }
154
    //-----------------------------------------------------------------------
155
    void MovableObject::_notifyMoved(void)
156
0
    {
157
        // Mark light list being dirty, simply decrease
158
        // counter by one for minimise overhead
159
0
        --mLightListUpdated;
160
161
        // Notify listener if exists
162
0
        if (mListener)
163
0
        {
164
0
            mListener->objectMoved(this);
165
0
        }
166
0
    }
167
    //-----------------------------------------------------------------------
168
    bool MovableObject::isVisible(void) const
169
0
    {
170
0
        if (!mVisible || mBeyondFarDistance || mRenderingDisabled)
171
0
            return false;
172
173
0
        SceneManager* sm = Root::getSingleton()._getCurrentSceneManager();
174
0
        if (sm && !(getVisibilityFlags() & sm->_getCombinedVisibilityMask()))
175
0
            return false;
176
177
0
        return true;
178
0
    }
179
    //-----------------------------------------------------------------------
180
    void MovableObject::_notifyCurrentCamera(Camera* cam)
181
0
    {
182
0
        if (mParentNode)
183
0
        {
184
0
            mBeyondFarDistance = false;
185
186
0
            if (cam->getUseRenderingDistance() && mUpperDistance > 0)
187
0
            {
188
0
                Real rad = getBoundingRadiusScaled();
189
0
                Real squaredDist = mParentNode->getSquaredViewDepth(cam->getLodCamera());
190
191
                // Max distance to still render
192
0
                Real maxDist = mUpperDistance + rad;
193
0
                if (squaredDist > Math::Sqr(maxDist))
194
0
                {
195
0
                    mBeyondFarDistance = true;
196
0
                }
197
0
            }
198
199
0
            if (!mBeyondFarDistance && cam->getUseMinPixelSize() && mMinPixelSize > 0)
200
0
            {
201
202
0
                Real pixelRatio = cam->getPixelDisplayRatio();
203
                
204
                //if ratio is relative to distance than the distance at which the object should be displayed
205
                //is the size of the radius divided by the ratio
206
                //get the size of the entity in the world
207
0
                Ogre::Vector3 objBound = getBoundingBox().getSize() * 
208
0
                            getParentNode()->_getDerivedScale();
209
                
210
                //We object are projected from 3 dimensions to 2. The shortest displayed dimension of 
211
                //as object will always be at most the second largest dimension of the 3 dimensional
212
                //bounding box.
213
                //The square calculation come both to get rid of minus sign and for improve speed
214
                //in the final calculation
215
0
                objBound.x = Math::Sqr(objBound.x);
216
0
                objBound.y = Math::Sqr(objBound.y);
217
0
                objBound.z = Math::Sqr(objBound.z);
218
0
                float sqrObjMedianSize = std::max(std::max(
219
0
                                    std::min(objBound.x,objBound.y),
220
0
                                    std::min(objBound.x,objBound.z)),
221
0
                                    std::min(objBound.y,objBound.z));
222
223
                //If we have a perspective camera calculations are done relative to distance
224
0
                Real sqrDistance = 1;
225
0
                if (cam->getProjectionType() == PT_PERSPECTIVE)
226
0
                {
227
0
                    sqrDistance = mParentNode->getSquaredViewDepth(cam->getLodCamera());
228
0
                }
229
230
                //Final Calculation to tell whether the object is to small
231
0
                mBeyondFarDistance =  sqrObjMedianSize < 
232
0
                            sqrDistance * Math::Sqr(pixelRatio * mMinPixelSize); 
233
0
            }
234
            
235
            // Construct event object
236
0
            MovableObjectLodChangedEvent evt;
237
0
            evt.movableObject = this;
238
0
            evt.camera = cam;
239
240
            // Notify LOD event listeners
241
0
            cam->getSceneManager()->_notifyMovableObjectLodChanged(evt);
242
243
0
        }
244
245
0
        mRenderingDisabled = mListener && !mListener->objectRendering(this, cam);
246
0
    }
247
    //-----------------------------------------------------------------------
248
    void MovableObject::setRenderQueueGroup(uint8 queueID)
249
0
    {
250
0
        assert(queueID <= RENDER_QUEUE_MAX && "Render queue out of range!");
251
0
        mRenderQueueID = queueID;
252
0
        mRenderQueueIDSet = true;
253
0
    }
254
255
    //-----------------------------------------------------------------------
256
    void MovableObject::setRenderQueueGroupAndPriority(uint8 queueID, ushort priority)
257
0
    {
258
0
        setRenderQueueGroup(queueID);
259
0
        mRenderQueuePriority = priority;
260
0
        mRenderQueuePrioritySet = true;
261
262
0
    }
263
264
    //-----------------------------------------------------------------------
265
    const Affine3& MovableObject::_getParentNodeFullTransform(void) const
266
0
    {
267
        
268
0
        if(mParentNode)
269
0
        {
270
            // object attached to a sceneNode
271
0
            return mParentNode->_getFullTransform();
272
0
        }
273
        // fallback
274
0
        return Affine3::IDENTITY;
275
0
    }
276
277
    Real MovableObject::getBoundingRadiusScaled() const
278
0
    {
279
0
        const Vector3& scl = mParentNode->_getDerivedScale();
280
0
        Real factor = std::max(std::max(std::abs(scl.x), std::abs(scl.y)), std::abs(scl.z));
281
0
        return getBoundingRadius() * factor;
282
0
    }
283
284
    //-----------------------------------------------------------------------
285
    const AxisAlignedBox& MovableObject::getWorldBoundingBox(bool derive) const
286
0
    {
287
0
        if (derive)
288
0
        {
289
0
            mWorldAABB = this->getBoundingBox();
290
0
            mWorldAABB.transform(_getParentNodeFullTransform());
291
0
        }
292
293
0
        return mWorldAABB;
294
295
0
    }
296
    //-----------------------------------------------------------------------
297
    const Sphere& MovableObject::getWorldBoundingSphere(bool derive) const
298
0
    {
299
0
        if (derive)
300
0
        {
301
0
            mWorldBoundingSphere.setRadius(getBoundingRadiusScaled());
302
0
            mWorldBoundingSphere.setCenter(mParentNode->_getDerivedPosition());
303
0
        }
304
0
        return mWorldBoundingSphere;
305
0
    }
306
    //-----------------------------------------------------------------------
307
    const LightList& MovableObject::queryLights(void) const
308
0
    {
309
        // Try listener first
310
0
        if (mListener)
311
0
        {
312
0
            const LightList* lightList =
313
0
                mListener->objectQueryLights(this);
314
0
            if (lightList)
315
0
            {
316
0
                return *lightList;
317
0
            }
318
0
        }
319
320
        // Query from parent entity if exists
321
0
        if (mParentIsTagPoint)
322
0
        {
323
0
            TagPoint* tp = static_cast<TagPoint*>(mParentNode);
324
0
            return tp->getParentEntity()->queryLights();
325
0
        }
326
327
0
        if (mParentNode)
328
0
        {
329
0
            SceneNode* sn = static_cast<SceneNode*>(mParentNode);
330
331
            // Make sure we only update this only if need.
332
0
            ulong frame = sn->getCreator()->_getLightsDirtyCounter();
333
0
            if (mLightListUpdated != frame)
334
0
            {
335
0
                mLightListUpdated = frame;
336
337
0
                sn->findLights(mLightList, getBoundingRadiusScaled(), this->getLightMask());
338
0
            }
339
0
        }
340
0
        else
341
0
        {
342
0
            mLightList.clear();
343
0
        }
344
345
0
        return mLightList;
346
0
    }
347
    //-----------------------------------------------------------------------
348
    Real MovableObject::getPointExtrusionDistance(const Light* l) const
349
0
    {
350
0
        if (mParentNode)
351
0
        {
352
            // exclude distance from the light to the shadow caster from the extrusion
353
0
            Real extrusionDistance = l->getAttenuationRange() - getWorldBoundingBox().distance(l->getDerivedPosition());
354
0
            if(extrusionDistance < 0)
355
0
                extrusionDistance = 0;
356
            
357
            // Take into account that extrusion would be done in object-space,
358
            // and non-uniformly scaled objects would cast non-uniformly scaled shadows.
359
0
            Matrix3 m3 = _getParentNodeFullTransform().linear();
360
0
            Real c0 = m3.GetColumn(0).squaredLength(), c1 = m3.GetColumn(1).squaredLength(), c2 = m3.GetColumn(2).squaredLength();
361
0
            Real minScale = Math::Sqrt(std::min(std::min(c0, c1), c2));
362
0
            Real maxScale = Math::Sqrt(std::max(std::max(c0, c1), c2));
363
0
            if(minScale > 0.0)
364
0
                extrusionDistance *= (maxScale / minScale);
365
            
366
0
            return extrusionDistance;
367
0
        }
368
0
        else
369
0
        {
370
0
            return 0;
371
0
        }
372
0
    }
373
    //-----------------------------------------------------------------------
374
    uint32 MovableObject::getTypeFlags(void) const
375
0
    {
376
0
        if (mCreator)
377
0
        {
378
0
            return mCreator->getTypeFlags();
379
0
        }
380
0
        else
381
0
        {
382
0
            return 0xFFFFFFFF;
383
0
        }
384
0
    }
385
    //---------------------------------------------------------------------
386
    void MovableObject::setLightMask(uint32 lightMask)
387
0
    {
388
0
        this->mLightMask = lightMask;
389
        //make sure to request a new light list from the scene manager if mask changed
390
0
        mLightListUpdated = 0;
391
0
    }
392
    //---------------------------------------------------------------------
393
    class MORecvShadVisitor : public Renderable::Visitor
394
    {
395
    public:
396
        bool anyReceiveShadows;
397
0
        MORecvShadVisitor() : anyReceiveShadows(false)
398
0
        {
399
400
0
        }
401
        void visit(Renderable* rend, ushort lodIndex, bool isDebug, Any* pAny = 0) override
402
0
        {
403
0
            const auto& mat = rend->getMaterial();
404
0
            bool techReceivesShadows = !mat || mat->getReceiveShadows();
405
0
            anyReceiveShadows = anyReceiveShadows || techReceivesShadows;
406
0
        }
407
    };
408
    //---------------------------------------------------------------------
409
    bool MovableObject::getReceivesShadows()
410
0
    {
411
0
        MORecvShadVisitor visitor;
412
0
        visitRenderables(&visitor);
413
0
        return visitor.anyReceiveShadows;       
414
415
0
    }
416
    //-----------------------------------------------------------------------
417
    //-----------------------------------------------------------------------
418
    MovableObject* MovableObjectFactory::createInstance(
419
        const String& name, SceneManager* manager, 
420
        const NameValuePairList* params)
421
0
    {
422
0
        MovableObject* m = createInstanceImpl(name, params);
423
0
        m->_notifyCreator(this);
424
0
        m->_notifyManager(manager);
425
0
        return m;
426
0
    }
427
428
429
}
430