Coverage Report

Created: 2026-05-16 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreInstancedEntity.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
#include "OgreInstancedEntity.h"
30
#include "OgreInstanceBatch.h"
31
#include "OgreSkeletonInstance.h"
32
#include "OgreAnimationState.h"
33
#include "OgreOptimisedUtil.h"
34
#include "OgreNameGenerator.h"
35
36
namespace Ogre
37
{
38
    const String MOT_INSTANCED_ENTITY = "InstancedEntity";
39
40
    NameGenerator InstancedEntity::msNameGenerator("");
41
42
    InstancedEntity::InstancedEntity( InstanceBatch *batchOwner, uint32 instanceID, InstancedEntity* sharedTransformEntity ) :
43
0
                MovableObject(),
44
0
                mInstanceId( instanceID ),
45
0
                mInUse( false ),
46
0
                mBatchOwner( batchOwner ),
47
0
                mAnimationState( 0 ),
48
0
                mSkeletonInstance( 0 ),
49
0
                mBoneMatrices(0),
50
0
                mBoneWorldMatrices(0),
51
0
                mFrameAnimationLastUpdated(std::numeric_limits<unsigned long>::max() - 1),
52
0
                mSharedTransformEntity( 0 ),
53
0
                mTransformLookupNumber(instanceID),
54
0
                mPosition(Vector3::ZERO),
55
0
                mDerivedLocalPosition(Vector3::ZERO),
56
0
                mOrientation(Quaternion::IDENTITY),
57
0
                mScale(Vector3::UNIT_SCALE),
58
0
                mMaxScaleLocal(1),
59
0
                mNeedTransformUpdate(true),
60
0
                mNeedAnimTransformUpdate(true),
61
0
                mUseLocalTransform(false)
62
63
    
64
0
    {
65
        //Use a static name generator to ensure this name stays unique (which may not happen
66
        //otherwise due to reparenting when defragmenting)
67
0
        mName = batchOwner->getName() + "/InstancedEntity_" + StringConverter::toString(mInstanceId) + "/"+
68
0
                msNameGenerator.generate();
69
70
0
        if (sharedTransformEntity)
71
0
        {
72
0
            sharedTransformEntity->shareTransformWith(this);
73
0
        }
74
0
        else
75
0
        {
76
0
            createSkeletonInstance();
77
0
        }
78
0
        updateTransforms();
79
0
    }
80
81
    InstancedEntity::~InstancedEntity()
82
0
    {
83
0
        unlinkTransform();
84
0
        destroySkeletonInstance();
85
0
    }
86
87
    bool InstancedEntity::shareTransformWith( InstancedEntity *slave )
88
0
    {
89
0
        if( !this->mBatchOwner->_getMeshRef()->hasSkeleton() ||
90
0
            !this->mBatchOwner->_getMeshRef()->getSkeleton() ||
91
0
            !this->mBatchOwner->_supportsSkeletalAnimation() )
92
0
        {
93
0
            return false;
94
0
        }
95
96
0
        if( this->mSharedTransformEntity  )
97
0
        {
98
0
            OGRE_EXCEPT( Exception::ERR_INVALID_STATE, "Attempted to share '" + mName + "' transforms "
99
0
                                            "with slave '" + slave->mName + "' but '" + mName +"' is "
100
0
                                            "already sharing. Hierarchical sharing not allowed.",
101
0
                                            "InstancedEntity::shareTransformWith" );
102
0
            return false;
103
0
        }
104
105
0
        if( this->mBatchOwner->_getMeshRef()->getSkeleton() !=
106
0
            slave->mBatchOwner->_getMeshRef()->getSkeleton() )
107
0
        {
108
0
            OGRE_EXCEPT( Exception::ERR_INVALID_STATE, "Sharing transforms requires both instanced"
109
0
                                            " entities to have the same skeleton",
110
0
                                            "InstancedEntity::shareTransformWith" );
111
0
            return false;
112
0
        }
113
114
0
        slave->unlinkTransform();
115
0
        slave->destroySkeletonInstance();
116
        
117
0
        slave->mSkeletonInstance    = this->mSkeletonInstance;
118
0
        slave->mAnimationState      = this->mAnimationState;
119
0
        slave->mBoneMatrices        = this->mBoneMatrices;
120
0
        if (mBatchOwner->useBoneWorldMatrices())
121
0
        {
122
0
            slave->mBoneWorldMatrices   = this->mBoneWorldMatrices;
123
0
        }
124
0
        slave->mSharedTransformEntity = this;
125
        //The sharing partners are kept in the parent entity 
126
0
        this->mSharingPartners.push_back( slave );
127
        
128
0
        slave->mBatchOwner->_markTransformSharingDirty();
129
130
0
        return true;
131
0
    }
132
    //-----------------------------------------------------------------------
133
    void InstancedEntity::stopSharingTransform()
134
0
    {
135
0
        if( mSharedTransformEntity )
136
0
        {
137
0
            stopSharingTransformAsSlave( true );
138
0
        }
139
0
        else
140
0
        {
141
            //Tell the ones sharing skeleton with us to use their own
142
0
            InstancedEntityVec::const_iterator itor = mSharingPartners.begin();
143
0
            InstancedEntityVec::const_iterator end  = mSharingPartners.end();
144
0
            while( itor != end )
145
0
            {
146
0
                (*itor)->stopSharingTransformAsSlave( false );
147
0
                ++itor;
148
0
            }
149
0
            mSharingPartners.clear();
150
0
        }
151
0
    }
152
    //-----------------------------------------------------------------------
153
    const String& InstancedEntity::getMovableType(void) const
154
0
    {
155
0
        return MOT_INSTANCED_ENTITY;
156
0
    }
157
    //-----------------------------------------------------------------------
158
    size_t InstancedEntity::getTransforms( Matrix4 *xform ) const
159
0
    {
160
0
        size_t retVal = 1;
161
162
        //When not attached, returns zero matrix to avoid rendering this one, not identity
163
0
        if( isVisible() && isInScene() )
164
0
        {
165
0
            if( !mSkeletonInstance )
166
0
            {
167
0
                *xform = mBatchOwner->useBoneWorldMatrices() ? 
168
0
                        _getParentNodeFullTransform() : Matrix4::IDENTITY;
169
0
            }
170
0
            else
171
0
            {
172
0
                Affine3* matrices = mBatchOwner->useBoneWorldMatrices() ? mBoneWorldMatrices : mBoneMatrices;
173
0
                const Mesh::IndexMap *indexMap = mBatchOwner->_getIndexToBoneMap();
174
0
                Mesh::IndexMap::const_iterator itor = indexMap->begin();
175
0
                Mesh::IndexMap::const_iterator end  = indexMap->end();
176
177
0
                while( itor != end )
178
0
                    *xform++ = matrices[*itor++];
179
180
0
                retVal = indexMap->size();
181
0
            }
182
0
        }
183
0
        else
184
0
        {
185
0
            if( mSkeletonInstance )
186
0
                retVal = mBatchOwner->_getIndexToBoneMap()->size();
187
188
0
            std::fill_n( xform, retVal, Matrix4::ZERO );
189
0
        }
190
191
0
        return retVal;
192
0
    }
193
    //-----------------------------------------------------------------------
194
    size_t InstancedEntity::getTransforms3x4( Matrix3x4f *xform ) const
195
0
    {
196
0
        size_t retVal;
197
        //When not attached, returns zero matrix to avoid rendering this one, not identity
198
0
        if( isVisible() && isInScene() )
199
0
        {
200
0
            if( !mSkeletonInstance )
201
0
            {
202
0
                const Affine3& mat = mBatchOwner->useBoneWorldMatrices() ?
203
0
                    _getParentNodeFullTransform() : Affine3::IDENTITY;
204
205
0
                *xform = Matrix3x4f(mat[0]);
206
0
                retVal = 12;
207
0
            }
208
0
            else
209
0
            {
210
0
                Affine3* matrices = mBatchOwner->useBoneWorldMatrices() ? mBoneWorldMatrices : mBoneMatrices;
211
0
                const Mesh::IndexMap *indexMap = mBatchOwner->_getIndexToBoneMap();
212
                
213
0
                for(auto i : *indexMap)
214
0
                {
215
0
                    *xform++ = Matrix3x4f(matrices[i][0]);
216
0
                }
217
218
0
                retVal = indexMap->size() * 4 * 3;
219
0
            }
220
0
        }
221
0
        else
222
0
        {
223
0
            if( mSkeletonInstance )
224
0
                retVal = mBatchOwner->_getIndexToBoneMap()->size() * 3 * 4;
225
0
            else
226
0
                retVal = 12;
227
            
228
0
            std::fill_n( xform, retVal/12, Matrix3x4f(Affine3::ZERO[0]) );
229
0
        }
230
231
0
        return retVal;
232
0
    }
233
    //-----------------------------------------------------------------------
234
    bool InstancedEntity::findVisible( Camera *camera ) const
235
0
    {
236
        //Object is active
237
0
        bool retVal = isInScene();
238
0
        if (retVal) 
239
0
        {
240
            //check object is explicitly visible
241
0
            retVal = isVisible();
242
243
            //Object's bounding box is viewed by the camera
244
0
            if( retVal && camera )
245
0
                retVal = camera->isVisible(Sphere(_getDerivedPosition(),getBoundingRadius() * getMaxScaleCoef()));
246
0
        }
247
248
0
        return retVal;
249
0
    }
250
    //-----------------------------------------------------------------------
251
    void InstancedEntity::createSkeletonInstance()
252
0
    {
253
        //Is mesh skeletally animated?
254
0
        if( mBatchOwner->_getMeshRef()->hasSkeleton() &&
255
0
            mBatchOwner->_getMeshRef()->getSkeleton() &&
256
0
            mBatchOwner->_supportsSkeletalAnimation() )
257
0
        {
258
0
            mSkeletonInstance = OGRE_NEW SkeletonInstance( mBatchOwner->_getMeshRef()->getSkeleton() );
259
0
            mSkeletonInstance->load();
260
261
0
            mBoneMatrices       = static_cast<Affine3*>(OGRE_MALLOC_SIMD( sizeof(Affine3) *
262
0
                                                                    mSkeletonInstance->getNumBones(),
263
0
                                                                    MEMCATEGORY_ANIMATION));
264
0
            if (mBatchOwner->useBoneWorldMatrices())
265
0
            {
266
0
                mBoneWorldMatrices  = static_cast<Affine3*>(OGRE_MALLOC_SIMD( sizeof(Affine3) *
267
0
                                                                    mSkeletonInstance->getNumBones(),
268
0
                                                                    MEMCATEGORY_ANIMATION));
269
0
                std::fill(mBoneWorldMatrices, mBoneWorldMatrices + mSkeletonInstance->getNumBones(), Affine3::IDENTITY);
270
0
            }
271
272
0
            mAnimationState = OGRE_NEW AnimationStateSet();
273
0
            mBatchOwner->_getMeshRef()->_initAnimationState( mAnimationState );
274
0
        }
275
0
    }
276
    //-----------------------------------------------------------------------
277
    void InstancedEntity::destroySkeletonInstance()
278
0
    {
279
0
        if( mSkeletonInstance )
280
0
        {
281
            //Tell the ones sharing skeleton with us to use their own
282
            //sharing partners will remove themselves from notifyUnlink
283
0
            while( mSharingPartners.empty() == false )
284
0
            {
285
0
                mSharingPartners.front()->stopSharingTransform();
286
0
            }
287
0
            mSharingPartners.clear();
288
289
0
            OGRE_DELETE mSkeletonInstance;
290
0
            OGRE_DELETE mAnimationState;
291
0
            OGRE_FREE_SIMD( mBoneMatrices, MEMCATEGORY_ANIMATION );
292
0
            OGRE_FREE_SIMD( mBoneWorldMatrices, MEMCATEGORY_ANIMATION );
293
294
0
            mSkeletonInstance   = 0;
295
0
            mAnimationState     = 0;
296
0
            mBoneMatrices       = 0;
297
0
            mBoneWorldMatrices  = 0;
298
0
        }
299
0
    }
300
    //-----------------------------------------------------------------------
301
    void InstancedEntity::stopSharingTransformAsSlave( bool notifyMaster )
302
0
    {
303
0
        unlinkTransform( notifyMaster );
304
0
        createSkeletonInstance();
305
0
    }
306
    //-----------------------------------------------------------------------
307
    void InstancedEntity::unlinkTransform( bool notifyMaster )
308
0
    {
309
0
        if( mSharedTransformEntity )
310
0
        {
311
            //Tell our master we're no longer his slave
312
0
            if( notifyMaster )
313
0
                mSharedTransformEntity->notifyUnlink( this );
314
0
            mBatchOwner->_markTransformSharingDirty();
315
316
0
            mSkeletonInstance   = 0;
317
0
            mAnimationState     = 0;
318
0
            mBoneMatrices       = 0;
319
0
            mBoneWorldMatrices  = 0;
320
0
            mSharedTransformEntity = 0;
321
0
        }
322
0
    }
323
    //-----------------------------------------------------------------------
324
    void InstancedEntity::notifyUnlink( const InstancedEntity *slave )
325
0
    {
326
        //Find the slave and remove it
327
0
        InstancedEntityVec::iterator itor = mSharingPartners.begin();
328
0
        InstancedEntityVec::iterator end  = mSharingPartners.end();
329
0
        while( itor != end )
330
0
        {
331
0
            if( *itor == slave )
332
0
            {
333
0
                std::swap(*itor,mSharingPartners.back());
334
0
                mSharingPartners.pop_back();
335
0
                break;
336
0
            }
337
338
0
            ++itor;
339
0
        }
340
0
    }
341
    //-----------------------------------------------------------------------
342
    const AxisAlignedBox& InstancedEntity::getBoundingBox(void) const
343
0
    {
344
        //TODO: Add attached objects (TagPoints) to the bbox
345
0
        return mBatchOwner->_getMeshReference()->getBounds();
346
0
    }
347
348
    //-----------------------------------------------------------------------
349
    Real InstancedEntity::getBoundingRadius(void) const
350
0
    {
351
0
        return mBatchOwner->_getMeshReference()->getBoundingSphereRadius();
352
0
    }
353
    //-----------------------------------------------------------------------
354
    Real InstancedEntity::getSquaredViewDepth( const Camera* cam ) const
355
0
    {
356
0
        return _getDerivedPosition().squaredDistance(cam->getDerivedPosition());
357
0
    }
358
    //-----------------------------------------------------------------------
359
    void InstancedEntity::_notifyMoved(void)
360
0
    {
361
0
        markTransformDirty();
362
0
        MovableObject::_notifyMoved();
363
0
        updateTransforms();
364
0
    }
365
366
    //-----------------------------------------------------------------------
367
    void InstancedEntity::_notifyAttached( Node* parent, bool isTagPoint )
368
0
    {
369
0
        markTransformDirty();
370
0
        MovableObject::_notifyAttached( parent, isTagPoint );
371
0
        updateTransforms();
372
0
    }
373
    //-----------------------------------------------------------------------
374
    AnimationState* InstancedEntity::getAnimationState(const String& name) const
375
0
    {
376
0
        if (!mAnimationState)
377
0
        {
378
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Entity is not animated",
379
0
                "InstancedEntity::getAnimationState");
380
0
        }
381
382
0
        return mAnimationState->getAnimationState(name);
383
0
    }
384
    //-----------------------------------------------------------------------
385
    AnimationStateSet* InstancedEntity::getAllAnimationStates(void) const
386
0
    {
387
0
        return mAnimationState;
388
0
    }
389
    //-----------------------------------------------------------------------
390
    bool InstancedEntity::_updateAnimation(void)
391
0
    {
392
0
        if (mSharedTransformEntity)
393
0
        {
394
0
            return mSharedTransformEntity->_updateAnimation();
395
0
        }
396
0
        else
397
0
        {
398
0
            const bool animationDirty =
399
0
                (mFrameAnimationLastUpdated != mAnimationState->getDirtyFrameNumber()) ||
400
0
                (mSkeletonInstance->getManualBonesDirty());
401
402
0
            if( animationDirty || (mNeedAnimTransformUpdate &&  mBatchOwner->useBoneWorldMatrices()))
403
0
            {
404
0
                mSkeletonInstance->setAnimationState( *mAnimationState );
405
0
                mSkeletonInstance->_getBoneMatrices( mBoneMatrices );
406
407
                // Cache last parent transform for next frame use too.
408
0
                if (mBatchOwner->useBoneWorldMatrices())
409
0
                {
410
0
                    OptimisedUtil::getImplementation()->concatenateAffineMatrices(
411
0
                                                    _getParentNodeFullTransform(),
412
0
                                                    mBoneMatrices,
413
0
                                                    mBoneWorldMatrices,
414
0
                                                    mSkeletonInstance->getNumBones() );
415
0
                    mNeedAnimTransformUpdate = false;
416
0
                }
417
                
418
0
                mFrameAnimationLastUpdated = mAnimationState->getDirtyFrameNumber();
419
420
0
                return true;
421
0
            }
422
0
        }
423
424
0
        return false;
425
0
    }
426
427
    //-----------------------------------------------------------------------
428
    void InstancedEntity::markTransformDirty()
429
0
    {
430
0
        mNeedTransformUpdate = true;
431
0
        mNeedAnimTransformUpdate = true; 
432
0
        mBatchOwner->_boundsDirty();
433
0
    }
434
435
    //---------------------------------------------------------------------------
436
    void InstancedEntity::setPosition(const Vector3& position, bool doUpdate) 
437
0
    { 
438
0
        mPosition = position; 
439
0
        mDerivedLocalPosition = position;
440
0
        mUseLocalTransform = true;
441
0
        markTransformDirty();
442
0
        if (doUpdate) updateTransforms();
443
0
    } 
444
445
    //---------------------------------------------------------------------------
446
    void InstancedEntity::setOrientation(const Quaternion& orientation, bool doUpdate) 
447
0
    { 
448
0
        mOrientation = orientation;  
449
0
        mUseLocalTransform = true;
450
0
        markTransformDirty();
451
0
        if (doUpdate) updateTransforms();
452
0
    } 
453
454
    //---------------------------------------------------------------------------
455
    void InstancedEntity::setScale(const Vector3& scale, bool doUpdate) 
456
0
    { 
457
0
        mScale = scale; 
458
0
        mMaxScaleLocal = std::max<Real>(std::max<Real>(
459
0
            Math::Abs(mScale.x), Math::Abs(mScale.y)), Math::Abs(mScale.z)); 
460
0
        mUseLocalTransform = true;
461
0
        markTransformDirty();
462
0
        if (doUpdate) updateTransforms();
463
0
    } 
464
465
    //---------------------------------------------------------------------------
466
    Real InstancedEntity::getMaxScaleCoef() const 
467
0
    { 
468
0
        return mMaxScaleLocal;
469
0
    }
470
471
    //---------------------------------------------------------------------------
472
    void InstancedEntity::updateTransforms()
473
0
    {
474
0
        if (mNeedTransformUpdate)
475
0
        {
476
0
            if (mUseLocalTransform)
477
0
            {
478
0
                if (mParentNode)
479
0
                {
480
0
                    const Vector3& parentPosition = mParentNode->_getDerivedPosition();
481
0
                    const Quaternion& parentOrientation = mParentNode->_getDerivedOrientation();
482
0
                    const Vector3& parentScale = mParentNode->_getDerivedScale();
483
484
0
                    Quaternion derivedOrientation = parentOrientation * mOrientation;
485
0
                    Vector3 derivedScale = parentScale * mScale;
486
0
                    mDerivedLocalPosition = parentOrientation * (parentScale * mPosition) + parentPosition;
487
488
0
                    mFullLocalTransform.makeTransform(mDerivedLocalPosition, derivedScale, derivedOrientation);
489
0
                }
490
0
                else
491
0
                {
492
0
                    mFullLocalTransform.makeTransform(mPosition,mScale,mOrientation);
493
0
                }
494
0
            } else {
495
0
                if (mParentNode) {
496
0
                    const Ogre::Vector3& parentScale = mParentNode->_getDerivedScale();
497
0
                    mMaxScaleLocal = std::max<Real>(std::max<Real>(
498
0
                            Math::Abs(parentScale.x), Math::Abs(parentScale.y)), Math::Abs(parentScale.z));
499
0
                }
500
0
            }
501
0
            mNeedTransformUpdate = false;
502
0
        }
503
0
    }
504
505
    //---------------------------------------------------------------------------
506
    void InstancedEntity::setInUse( bool used )
507
0
    {
508
0
        mInUse = used;
509
        //Remove the use of local transform if the object is deleted
510
0
        mUseLocalTransform &= used;
511
0
    }
512
    //---------------------------------------------------------------------------
513
    void InstancedEntity::setCustomParam( unsigned char idx, const Vector4f &newParam )
514
0
    {
515
0
        mBatchOwner->_setCustomParam( this, idx, newParam );
516
0
    }
517
    //---------------------------------------------------------------------------
518
    const Vector4f& InstancedEntity::getCustomParam( unsigned char idx )
519
0
    {
520
0
        return mBatchOwner->_getCustomParam( this, idx );
521
0
    }
522
}