Coverage Report

Created: 2026-10-28 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreInstanceManager.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 "OgreInstanceManager.h"
30
#include "OgreInstanceBatchHW.h"
31
#include "OgreInstanceBatchHW_VTF.h"
32
#include "OgreInstanceBatchShader.h"
33
#include "OgreInstanceBatchVTF.h"
34
35
namespace Ogre
36
{
37
    InstanceManager::InstanceManager( const String &customName, SceneManager *sceneManager,
38
                                        const String &meshName, const String &groupName,
39
                                        InstancingTechnique instancingTechnique, uint16 instancingFlags,
40
                                        size_t instancesPerBatch, unsigned short subMeshIdx, bool useBoneMatrixLookup ) :
41
0
                mName( customName ),
42
0
                mIdCount( 0 ),
43
0
                mInstancesPerBatch( instancesPerBatch ),
44
0
                mInstancingTechnique( instancingTechnique ),
45
0
                mInstancingFlags( instancingFlags ),
46
0
                mSubMeshIdx( subMeshIdx ),
47
0
                mSceneManager( sceneManager ),
48
0
                mMaxLookupTableInstances(16),
49
0
                mNumCustomParams( 0 )
50
0
    {
51
0
        mMeshReference = MeshManager::getSingleton().load( meshName, groupName );
52
53
0
        if(mMeshReference->sharedVertexData)
54
0
            unshareVertices(mMeshReference);
55
56
0
        if( mMeshReference->hasSkeleton() && mMeshReference->getSkeleton() )
57
0
            mMeshReference->getSubMesh(mSubMeshIdx)->_compileBoneAssignments();
58
0
    }
59
                
60
    InstanceManager::~InstanceManager()
61
0
    {
62
        //Remove all batches from all materials we created
63
0
        for (auto& i : mInstanceBatches)
64
0
        {
65
0
            for (auto *it : i.second)
66
0
                OGRE_DELETE it;
67
0
        }
68
0
    }
69
    //----------------------------------------------------------------------
70
    void InstanceManager::setInstancesPerBatch( size_t instancesPerBatch )
71
0
    {
72
0
        OgreAssert(mInstanceBatches.empty(), "can only be changed before building the batch");
73
0
        mInstancesPerBatch = instancesPerBatch;
74
0
    }
75
76
    //----------------------------------------------------------------------
77
    void InstanceManager::setMaxLookupTableInstances( size_t maxLookupTableInstances )
78
0
    {
79
0
        OgreAssert(mInstanceBatches.empty(), "can only be changed before building the batch");
80
0
        mMaxLookupTableInstances = maxLookupTableInstances;
81
0
    }
82
    
83
    //----------------------------------------------------------------------
84
    void InstanceManager::setNumCustomParams( unsigned char numCustomParams )
85
0
    {
86
0
        OgreAssert(mInstanceBatches.empty(), "can only be changed before building the batch");
87
0
        mNumCustomParams = numCustomParams;
88
0
    }
89
    //----------------------------------------------------------------------
90
    size_t InstanceManager::getMaxOrBestNumInstancesPerBatch( const String &materialName, size_t suggestedSize,
91
                                                                uint16 flags )
92
0
    {
93
        //Get the material
94
0
        MaterialPtr mat = MaterialManager::getSingleton().getByName( materialName,
95
0
                                                                    mMeshReference->getGroup() );
96
0
        InstanceBatch *batch = 0;
97
98
        //Base material couldn't be found
99
0
        if( !mat )
100
0
            return 0;
101
102
0
        switch( mInstancingTechnique )
103
0
        {
104
0
        case ShaderBased:
105
0
            batch = OGRE_NEW InstanceBatchShader( this, mMeshReference, mat, suggestedSize,
106
0
                                                    0, mName + "/TempBatch" );
107
0
            break;
108
0
        case TextureVTF:
109
0
            batch = OGRE_NEW InstanceBatchVTF( this, mMeshReference, mat, suggestedSize,
110
0
                                                    0, mName + "/TempBatch" );
111
0
            static_cast<InstanceBatchVTF*>(batch)->setBoneDualQuaternions((mInstancingFlags & IM_USEBONEDUALQUATERNIONS) != 0);
112
0
            static_cast<InstanceBatchVTF*>(batch)->setUseOneWeight((mInstancingFlags & IM_USEONEWEIGHT) != 0);
113
0
            static_cast<InstanceBatchVTF*>(batch)->setForceOneWeight((mInstancingFlags & IM_FORCEONEWEIGHT) != 0);
114
0
            break;
115
0
        case HWInstancingBasic:
116
0
            batch = OGRE_NEW InstanceBatchHW( this, mMeshReference, mat, suggestedSize,
117
0
                                                    0, mName + "/TempBatch" );
118
0
            break;
119
0
        case HWInstancingVTF:
120
0
            batch = OGRE_NEW InstanceBatchHW_VTF( this, mMeshReference, mat, suggestedSize,
121
0
                                                    0, mName + "/TempBatch" );
122
0
            static_cast<InstanceBatchHW_VTF*>(batch)->setBoneMatrixLookup((mInstancingFlags & IM_VTFBONEMATRIXLOOKUP) != 0, mMaxLookupTableInstances);
123
0
            static_cast<InstanceBatchHW_VTF*>(batch)->setBoneDualQuaternions((mInstancingFlags & IM_USEBONEDUALQUATERNIONS) != 0);
124
0
            static_cast<InstanceBatchHW_VTF*>(batch)->setUseOneWeight((mInstancingFlags & IM_USEONEWEIGHT) != 0);
125
0
            static_cast<InstanceBatchHW_VTF*>(batch)->setForceOneWeight((mInstancingFlags & IM_FORCEONEWEIGHT) != 0);
126
0
            break;
127
0
        default:
128
0
            OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,
129
0
                    "Unimplemented instancing technique: " +
130
0
                    StringConverter::toString(mInstancingTechnique),
131
0
                    "InstanceBatch::getMaxOrBestNumInstancesPerBatches()");
132
0
        }
133
134
0
        const size_t retVal = batch->calculateMaxNumInstances( mMeshReference->getSubMesh(mSubMeshIdx),
135
0
                                                                flags );
136
137
0
        OGRE_DELETE batch;
138
139
0
        return retVal;
140
0
    }
141
    //----------------------------------------------------------------------
142
    InstancedEntity* InstanceManager::createInstancedEntity( const String &materialName )
143
0
    {
144
0
        InstanceBatch *instanceBatch;
145
146
0
        if( mInstanceBatches.empty() ||
147
0
            (mInstanceBatches[materialName].size () == 0))
148
0
            instanceBatch = buildNewBatch( materialName, true );
149
0
        else
150
0
            instanceBatch = getFreeBatch( materialName );
151
152
0
        return instanceBatch->createInstancedEntity();
153
0
    }
154
    //-----------------------------------------------------------------------
155
    inline InstanceBatch* InstanceManager::getFreeBatch( const String &materialName )
156
0
    {
157
0
        InstanceBatchVec &batchVec = mInstanceBatches[materialName];
158
0
        for (auto *b : batchVec)
159
0
        {
160
0
            if(!b->isBatchFull())
161
0
                return b;
162
0
        }
163
164
        //None found, or they're all full
165
0
        return buildNewBatch( materialName, false );
166
0
    }
167
    //-----------------------------------------------------------------------
168
    InstanceBatch* InstanceManager::buildNewBatch( const String &materialName, bool firstTime )
169
0
    {
170
        //Get the bone to index map for the batches
171
0
        Mesh::IndexMap &idxMap = mMeshReference->getSubMesh(mSubMeshIdx)->blendIndexToBoneIndexMap;
172
0
        idxMap = idxMap.empty() ? mMeshReference->sharedBlendIndexToBoneIndexMap : idxMap;
173
174
        //Get the material
175
0
        MaterialPtr mat = MaterialManager::getSingleton().getByName( materialName,
176
0
                                                                    mMeshReference->getGroup() );
177
178
        //Get the array of batches grouped by this material
179
0
        InstanceBatchVec &materialInstanceBatch = mInstanceBatches[materialName];
180
181
0
        InstanceBatch *batch = 0;
182
183
0
        switch( mInstancingTechnique )
184
0
        {
185
0
        case ShaderBased:
186
0
            batch = OGRE_NEW InstanceBatchShader( this, mMeshReference, mat, mInstancesPerBatch,
187
0
                                                    &idxMap, mName + "/InstanceBatch_" +
188
0
                                                    StringConverter::toString(mIdCount++) );
189
0
            break;
190
0
        case TextureVTF:
191
0
            batch = OGRE_NEW InstanceBatchVTF( this, mMeshReference, mat, mInstancesPerBatch,
192
0
                                                    &idxMap, mName + "/InstanceBatch_" +
193
0
                                                    StringConverter::toString(mIdCount++) );
194
0
            static_cast<InstanceBatchVTF*>(batch)->setBoneDualQuaternions((mInstancingFlags & IM_USEBONEDUALQUATERNIONS) != 0);
195
0
            static_cast<InstanceBatchVTF*>(batch)->setUseOneWeight((mInstancingFlags & IM_USEONEWEIGHT) != 0);
196
0
            static_cast<InstanceBatchVTF*>(batch)->setForceOneWeight((mInstancingFlags & IM_FORCEONEWEIGHT) != 0);
197
0
            break;
198
0
        case HWInstancingBasic:
199
0
            batch = OGRE_NEW InstanceBatchHW( this, mMeshReference, mat, mInstancesPerBatch,
200
0
                                                    &idxMap, mName + "/InstanceBatch_" +
201
0
                                                    StringConverter::toString(mIdCount++) );
202
0
            break;
203
0
        case HWInstancingVTF:
204
0
            batch = OGRE_NEW InstanceBatchHW_VTF( this, mMeshReference, mat, mInstancesPerBatch,
205
0
                                                    &idxMap, mName + "/InstanceBatch_" +
206
0
                                                    StringConverter::toString(mIdCount++) );
207
0
            static_cast<InstanceBatchHW_VTF*>(batch)->setBoneMatrixLookup((mInstancingFlags & IM_VTFBONEMATRIXLOOKUP) != 0, mMaxLookupTableInstances);
208
0
            static_cast<InstanceBatchHW_VTF*>(batch)->setBoneDualQuaternions((mInstancingFlags & IM_USEBONEDUALQUATERNIONS) != 0);
209
0
            static_cast<InstanceBatchHW_VTF*>(batch)->setUseOneWeight((mInstancingFlags & IM_USEONEWEIGHT) != 0);
210
0
            static_cast<InstanceBatchHW_VTF*>(batch)->setForceOneWeight((mInstancingFlags & IM_FORCEONEWEIGHT) != 0);
211
0
            break;
212
0
        default:
213
0
            OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,
214
0
                    "Unimplemented instancing technique: " +
215
0
                    StringConverter::toString(mInstancingTechnique),
216
0
                    "InstanceBatch::buildNewBatch()");
217
0
        }
218
219
0
        batch->_notifyManager( mSceneManager );
220
221
222
0
        if( !firstTime )
223
0
        {
224
            //TODO: Check different materials have the same mInstancesPerBatch upper limit
225
            //otherwise we can't share
226
0
            batch->buildFrom( mMeshReference->getSubMesh(mSubMeshIdx), mSharedRenderOperation );
227
0
        }
228
0
        else
229
0
        {
230
            //Ensure we don't request more than we can
231
0
            const size_t maxInstPerBatch = batch->calculateMaxNumInstances( mMeshReference->
232
0
                                                        getSubMesh(mSubMeshIdx), mInstancingFlags );
233
0
            mInstancesPerBatch = std::min( maxInstPerBatch, mInstancesPerBatch );
234
0
            batch->_setInstancesPerBatch( mInstancesPerBatch );
235
236
0
            OgreAssert(mInstancesPerBatch, "unsupported instancing technique");
237
            //TODO: Create a "merge" function that merges all submeshes into one big submesh
238
            //instead of just sending submesh #0
239
240
            //Get the RenderOperation to be shared with further instances.
241
0
            mSharedRenderOperation = batch->build( mMeshReference->getSubMesh(mSubMeshIdx) );
242
0
        }
243
244
0
        const BatchSettings &batchSettings = mBatchSettings[materialName];
245
0
        batch->setCastShadows( batchSettings.setting[CAST_SHADOWS] );
246
247
        //Batches need to be part of a scene node so that their renderable can be rendered
248
0
        SceneNode *sceneNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
249
0
        sceneNode->attachObject( batch );
250
0
        sceneNode->showBoundingBox( batchSettings.setting[SHOW_BOUNDINGBOX] );
251
252
0
        materialInstanceBatch.push_back( batch );
253
254
0
        return batch;
255
0
    }
256
    //-----------------------------------------------------------------------
257
    void InstanceManager::cleanupEmptyBatches(void)
258
0
    {
259
        //Do this now to avoid any dangling pointer inside mDirtyBatches
260
0
        _updateDirtyBatches();
261
262
0
        InstanceBatchMap::iterator itor = mInstanceBatches.begin();
263
0
        InstanceBatchMap::iterator end  = mInstanceBatches.end();
264
265
0
        while( itor != end )
266
0
        {
267
0
            InstanceBatchVec::iterator it = itor->second.begin();
268
0
            InstanceBatchVec::iterator en = itor->second.end();
269
270
0
            while( it != en )
271
0
            {
272
0
                if( (*it)->isBatchUnused() )
273
0
                {
274
0
                    OGRE_DELETE *it;
275
                    //Remove it from the list swapping with the last element and popping back
276
0
                    size_t idx = it - itor->second.begin();
277
0
                    *it = itor->second.back();
278
0
                    itor->second.pop_back();
279
280
                    //Restore invalidated iterators
281
0
                    it = itor->second.begin() + idx;
282
0
                    en = itor->second.end();
283
0
                }
284
0
                else
285
0
                    ++it;
286
0
            }
287
288
0
            ++itor;
289
0
        }
290
291
        //By this point it may happen that all mInstanceBatches' objects are also empty
292
        //however if we call mInstanceBatches.clear(), next time we'll create an InstancedObject
293
        //we'll end up calling buildFirstTime() instead of buildNewBatch(), which is not the idea
294
        //(takes more time and will leak the shared render operation)
295
0
    }
296
    //-----------------------------------------------------------------------
297
    void InstanceManager::defragmentBatches( bool optimizeCull,
298
                                                InstanceBatch::InstancedEntityVec &usedEntities,
299
                                                InstanceBatch::CustomParamsVec &usedParams,
300
                                                InstanceBatchVec &fragmentedBatches )
301
0
    {
302
0
        InstanceBatchVec::iterator itor = fragmentedBatches.begin();
303
0
        InstanceBatchVec::iterator end  = fragmentedBatches.end();
304
305
0
        while( itor != end && !usedEntities.empty() )
306
0
        {
307
0
            if( !(*itor)->isStatic() )
308
0
                (*itor)->_defragmentBatch( optimizeCull, usedEntities, usedParams );
309
0
            ++itor;
310
0
        }
311
312
0
        InstanceBatchVec::iterator lastImportantBatch = itor;
313
314
0
        while( itor != end )
315
0
        {
316
0
            if( !(*itor)->isStatic() )
317
0
            {
318
                //If we get here, this means we hit remaining batches which will be unused.
319
                //Destroy them
320
                //Call this to avoid freeing InstancedEntities that were just reparented
321
0
                (*itor)->_defragmentBatchDiscard();
322
0
                OGRE_DELETE *itor;
323
0
            }
324
0
            else
325
0
            {
326
                //This isn't a meaningless batch, move it forward so it doesn't get wipe
327
                //when we resize the container (faster than removing element by element)
328
0
                *lastImportantBatch++ = *itor;
329
0
            }
330
331
0
            ++itor;
332
0
        }
333
334
        //Remove remaining batches all at once from the vector
335
0
        const size_t remainingBatches = end - lastImportantBatch;
336
0
        fragmentedBatches.resize( fragmentedBatches.size() - remainingBatches );
337
0
    }
338
    //-----------------------------------------------------------------------
339
    void InstanceManager::defragmentBatches( bool optimizeCulling )
340
0
    {
341
        //Do this now to avoid any dangling pointer inside mDirtyBatches
342
0
        _updateDirtyBatches();
343
344
        //Do this for every material
345
0
        for (auto& b : mInstanceBatches)
346
0
        {
347
0
            InstanceBatch::InstancedEntityVec   usedEntities;
348
0
            InstanceBatch::CustomParamsVec      usedParams;
349
0
            usedEntities.reserve(b.second.size() * mInstancesPerBatch );
350
351
            //Collect all Instanced Entities being used by _all_ batches from this material
352
0
            for (auto *it : b.second)
353
0
            {
354
                //Don't collect instances from static batches, we assume they're correctly set
355
                //Plus, we don't want to put InstancedEntities from non-static into static batches
356
0
                if (!it->isStatic())
357
0
                    it->getInstancedEntitiesInUse(usedEntities, usedParams);
358
0
            }
359
360
0
            defragmentBatches(optimizeCulling, usedEntities, usedParams, b.second );
361
0
        }
362
0
    }
363
    //-----------------------------------------------------------------------
364
    void InstanceManager::setSetting( BatchSettingId id, bool value, const String &materialName )
365
0
    {
366
0
        assert( id < NUM_SETTINGS );
367
368
0
        if( materialName == BLANKSTRING )
369
0
        {
370
            //Setup all existing materials
371
0
            for (auto& i : mInstanceBatches)
372
0
            {
373
0
                mBatchSettings[i.first].setting[id] = value;
374
0
                applySettingToBatches( id, value, i.second );
375
0
            }
376
0
        }
377
0
        else
378
0
        {
379
            //Setup a given material
380
0
            mBatchSettings[materialName].setting[id] = value;
381
382
0
            InstanceBatchMap::const_iterator itor = mInstanceBatches.find( materialName );
383
            //Don't crash or throw if the batch with that material hasn't been created yet
384
0
            if( itor != mInstanceBatches.end() )
385
0
                applySettingToBatches( id, value, itor->second );
386
0
        }
387
0
    }
388
    //-----------------------------------------------------------------------
389
    bool InstanceManager::getSetting( BatchSettingId id, const String &materialName ) const
390
0
    {
391
0
        assert( id < NUM_SETTINGS );
392
393
0
        BatchSettingsMap::const_iterator itor = mBatchSettings.find( materialName );
394
0
        if( itor != mBatchSettings.end() )
395
0
            return itor->second.setting[id]; //Return current setting
396
397
        //Return default
398
0
        return BatchSettings().setting[id];
399
0
    }
400
    bool InstanceManager::hasSettings(const String& materialName) const
401
0
    {
402
0
        return mBatchSettings.find(materialName) != mBatchSettings.end();
403
0
    }
404
    //-----------------------------------------------------------------------
405
    void InstanceManager::applySettingToBatches( BatchSettingId id, bool value,
406
                                                 const InstanceBatchVec &container )
407
0
    {
408
0
        for (auto *s : container)
409
0
        {
410
0
            switch(id)
411
0
            {
412
0
            case CAST_SHADOWS:
413
0
                s->setCastShadows(value);
414
0
                break;
415
0
            case SHOW_BOUNDINGBOX:
416
0
                s->getParentSceneNode()->showBoundingBox(value);
417
0
                break;
418
0
            default:
419
0
                break;
420
0
            }
421
0
        }
422
0
    }
423
    //-----------------------------------------------------------------------
424
    void InstanceManager::setBatchesAsStaticAndUpdate( bool bStatic )
425
0
    {
426
0
        for (auto& b : mInstanceBatches)
427
0
        {
428
0
            for (auto *it : b.second)
429
0
            {
430
0
                it->setStaticAndUpdate(bStatic);
431
0
            }
432
0
        }
433
0
    }
434
    //-----------------------------------------------------------------------
435
    void InstanceManager::_addDirtyBatch( InstanceBatch *dirtyBatch )
436
0
    {
437
0
        if( mDirtyBatches.empty() )
438
0
            mSceneManager->_addDirtyInstanceManager( this );
439
440
0
        mDirtyBatches.push_back( dirtyBatch );
441
0
    }
442
    //-----------------------------------------------------------------------
443
    void InstanceManager::_updateDirtyBatches(void)
444
0
    {
445
0
        for (auto *d : mDirtyBatches)
446
0
        {
447
0
            d->_updateBounds();
448
0
        }
449
0
        mDirtyBatches.clear();
450
0
    }
451
    //-----------------------------------------------------------------------
452
    // Helper functions to unshare the vertices
453
    //-----------------------------------------------------------------------
454
    typedef std::map<uint32, uint32> IndicesMap;
455
456
    template< typename TIndexType >
457
    void collectUsedIndices(IndicesMap& indicesMap, IndexData* idxData)
458
0
    {
459
0
        HardwareBufferLockGuard indexLock(idxData->indexBuffer,
460
0
                                          idxData->indexStart * sizeof(TIndexType),
461
0
                                          idxData->indexCount * sizeof(TIndexType),
462
0
                                          HardwareBuffer::HBL_READ_ONLY);
463
0
        TIndexType *data = (TIndexType*)indexLock.pData;
464
465
0
        for (size_t i = 0; i < idxData->indexCount; i++)
466
0
        {
467
0
            TIndexType index = data[i];
468
0
            if (indicesMap.find(index) == indicesMap.end())
469
0
            {
470
                //We need to guarantee that the size is read before an entry is added, hence these are on separate lines.
471
0
                uint32 size = (uint32)(indicesMap.size());
472
0
                indicesMap[index] = size;
473
0
            }
474
0
        }
475
0
    }
Unexecuted instantiation: void Ogre::collectUsedIndices<unsigned short>(std::__1::map<unsigned int, unsigned int, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, unsigned int> > >&, Ogre::IndexData*)
Unexecuted instantiation: void Ogre::collectUsedIndices<unsigned int>(std::__1::map<unsigned int, unsigned int, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, unsigned int> > >&, Ogre::IndexData*)
476
    //-----------------------------------------------------------------------
477
    template< typename TIndexType >
478
    void copyIndexBuffer(IndexData* idxData, const IndicesMap& indicesMap, uint32 indexStart)
479
0
    {
480
0
        size_t start = std::max(indexStart, idxData->indexStart);
481
0
        size_t count = idxData->indexCount - (start - idxData->indexStart);
482
483
        //We get errors if we try to lock a zero size buffer.
484
0
        if (count == 0) {
485
0
            return;
486
0
        }
487
0
        HardwareBufferLockGuard indexLock(idxData->indexBuffer,
488
0
                                          start * sizeof(TIndexType),
489
0
                                          count * sizeof(TIndexType),
490
0
                                          HardwareBuffer::HBL_NORMAL);
491
0
        TIndexType *data = (TIndexType*)indexLock.pData;
492
493
0
        for (size_t i = 0; i < count; i++)
494
0
        {
495
            //Access data and write on two separate lines, to avoid compiler confusion.
496
0
            auto originalPos = data[i];
497
0
            auto indexEntry = indicesMap.find(originalPos);
498
0
            data[i] = (TIndexType)(indexEntry->second);
499
0
        }
500
0
    }
Unexecuted instantiation: void Ogre::copyIndexBuffer<unsigned short>(Ogre::IndexData*, std::__1::map<unsigned int, unsigned int, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, unsigned int> > > const&, unsigned int)
Unexecuted instantiation: void Ogre::copyIndexBuffer<unsigned int>(Ogre::IndexData*, std::__1::map<unsigned int, unsigned int, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, unsigned int> > > const&, unsigned int)
501
    //-----------------------------------------------------------------------
502
    void InstanceManager::unshareVertices(const Ogre::MeshPtr &mesh)
503
0
    {
504
        // Retrieve data to copy bone assignments
505
0
        const Mesh::VertexBoneAssignmentList& boneAssignments = mesh->getBoneAssignments();
506
0
        size_t curVertexOffset = 0;
507
508
        // Access shared vertices
509
0
        VertexData* sharedVertexData = mesh->sharedVertexData;
510
511
0
        for (size_t subMeshIdx = 0; subMeshIdx < mesh->getNumSubMeshes(); subMeshIdx++)
512
0
        {
513
0
            SubMesh *subMesh = mesh->getSubMesh(subMeshIdx);
514
515
0
            IndexData *indexData = subMesh->indexData;
516
0
            HardwareIndexBuffer::IndexType idxType = indexData->indexBuffer->getType();
517
0
            IndicesMap indicesMap;
518
0
            if (idxType == HardwareIndexBuffer::IT_16BIT) {
519
0
                collectUsedIndices<uint16>(indicesMap, indexData);
520
0
            } else {
521
0
                collectUsedIndices<uint32>(indicesMap, indexData);
522
0
            }
523
524
            //Also collect indices for all LOD faces.
525
0
            for (auto& lodIndex : subMesh->mLodFaceList) {
526
                //Typically the LOD indices would use the same buffer type as the main index. But we'll check to make extra sure.
527
0
                if (lodIndex->indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT) {
528
0
                    collectUsedIndices<uint16>(indicesMap, lodIndex);
529
0
                } else {
530
0
                    collectUsedIndices<uint32>(indicesMap, lodIndex);
531
0
                }
532
0
            }
533
534
0
            VertexData *newVertexData = new VertexData();
535
0
            newVertexData->vertexCount = indicesMap.size();
536
0
            newVertexData->vertexDeclaration = sharedVertexData->vertexDeclaration->clone();
537
538
0
            for (uint16 bufIdx = 0; bufIdx < uint16(sharedVertexData->vertexBufferBinding->getBufferCount()); bufIdx++)
539
0
            {
540
0
                HardwareVertexBufferSharedPtr sharedVertexBuffer = sharedVertexData->vertexBufferBinding->getBuffer(bufIdx);
541
0
                size_t vertexSize = sharedVertexBuffer->getVertexSize();                
542
543
0
                HardwareVertexBufferSharedPtr newVertexBuffer = HardwareBufferManager::getSingleton().createVertexBuffer
544
0
                    (vertexSize, newVertexData->vertexCount, sharedVertexBuffer->getUsage(), sharedVertexBuffer->hasShadowBuffer());
545
546
0
                HardwareBufferLockGuard oldLock(sharedVertexBuffer, 0, sharedVertexData->vertexCount * vertexSize, HardwareBuffer::HBL_READ_ONLY);
547
0
                HardwareBufferLockGuard newLock(newVertexBuffer, 0, newVertexData->vertexCount * vertexSize, HardwareBuffer::HBL_NORMAL);
548
549
0
                for (auto& id : indicesMap)
550
0
                {
551
0
                    memcpy((uint8*)newLock.pData + vertexSize * id.second,
552
0
                           (uint8*)oldLock.pData + vertexSize * id.first, vertexSize);
553
0
                }
554
555
0
                newVertexData->vertexBufferBinding->setBinding(bufIdx, newVertexBuffer);
556
0
            }
557
558
0
            if (idxType == HardwareIndexBuffer::IT_16BIT)
559
0
            {
560
0
                copyIndexBuffer<uint16>(indexData, indicesMap, 0);
561
0
            }
562
0
            else
563
0
            {
564
0
                copyIndexBuffer<uint32>(indexData, indicesMap, 0);
565
0
            }
566
567
            //Need to adjust all of the LOD faces too.
568
0
            size_t lastIndexEnd = 0;
569
0
            for (size_t i = 0; i < subMesh->mLodFaceList.size(); ++i) {
570
0
        auto lodIndex = subMesh->mLodFaceList[i];
571
        //When using "generated" mesh lods, the indices are shared, so that index[n] would overlap with
572
        //index[n]. We need to take this into account to make sure we don't process data twice (with incorrect
573
        //data as a result). We check if the index either is the first one, or if it has a different buffer
574
        //than the previous one we processed. Since the overlap seems to be progressing we only need to keep
575
        // track of the last used index.
576
0
                if (i == 0 || lodIndex->indexBuffer != subMesh->mLodFaceList[i - 1]->indexBuffer) {
577
0
                    lastIndexEnd = 0;
578
0
                }
579
580
                //Typically the LOD indices would use the same buffer type as the main index. But we'll check to make extra sure.
581
0
                if (lodIndex->indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT) {
582
0
                    copyIndexBuffer<uint16>(lodIndex, indicesMap, lastIndexEnd);
583
0
                } else {
584
0
                    copyIndexBuffer<uint32>(lodIndex, indicesMap, lastIndexEnd);
585
0
                }
586
0
                lastIndexEnd = lodIndex->indexStart + lodIndex->indexCount;
587
0
      }
588
589
            // Store new attributes
590
0
            subMesh->resetVertexData(newVertexData);
591
592
            // Transfer bone assignments to the submesh
593
0
            size_t offset = curVertexOffset + newVertexData->vertexCount;
594
0
            for (auto& b : boneAssignments)
595
0
            {
596
0
                size_t vertexIdx = b.first;
597
0
                if (vertexIdx > offset)
598
0
                    break;
599
600
0
                VertexBoneAssignment boneAssignment = b.second;
601
0
                boneAssignment.vertexIndex = static_cast<unsigned int>(boneAssignment.vertexIndex - curVertexOffset);
602
0
                subMesh->addBoneAssignment(boneAssignment);
603
0
            }
604
0
            curVertexOffset = newVertexData->vertexCount + 1;
605
0
        }
606
607
        // Release shared vertex data
608
0
        mesh->resetVertexData();
609
0
        mesh->clearBoneAssignments();
610
611
0
        if( mesh->isEdgeListBuilt() )
612
0
        {
613
0
            mesh->freeEdgeList();
614
0
            mesh->buildEdgeList();
615
0
        }
616
0
    }
617
    //-----------------------------------------------------------------------
618
    InstanceManager::InstanceBatchIterator InstanceManager::getInstanceBatchIterator( const String &materialName ) const
619
0
    {
620
0
        InstanceBatchMap::const_iterator it = mInstanceBatches.find( materialName );
621
0
        if(it != mInstanceBatches.end())
622
0
            return InstanceBatchIterator( it->second.begin(), it->second.end() );
623
624
0
        OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "Cannot create instance batch iterator. "
625
0
                    "Material " + materialName + " cannot be found");
626
0
    }
627
}