Coverage Report

Created: 2026-07-16 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreMesh.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 "OgreSkeletonManager.h"
31
#include "OgreEdgeListBuilder.h"
32
#include "OgreAnimation.h"
33
#include "OgreAnimationState.h"
34
#include "OgreAnimationTrack.h"
35
#include "OgreOptimisedUtil.h"
36
#include "OgreTangentSpaceCalc.h"
37
#include "OgreLodStrategyManager.h"
38
#include "OgrePixelCountLodStrategy.h"
39
#include "OgreDefaultHardwareBufferManager.h"
40
41
namespace Ogre {
42
    //-----------------------------------------------------------------------
43
    Mesh::Mesh(ResourceManager* creator, const String& name, ResourceHandle handle,
44
        const String& group, bool isManual, ManualResourceLoader* loader)
45
4.96k
        : Resource(creator, name, handle, group, isManual, loader),
46
4.96k
        mBoundRadius(0.0f),
47
4.96k
        mBoneBoundingRadius(0.0f),
48
4.96k
        mBoneAssignmentsOutOfDate(false),
49
4.96k
        mHasManualLodLevel(false),
50
4.96k
        mLodStrategy(LodStrategyManager::getSingleton().getDefaultStrategy()),
51
4.96k
        mBufferManager(0),
52
4.96k
        mVertexBufferUsage(HBU_GPU_ONLY),
53
4.96k
        mIndexBufferUsage(HBU_GPU_ONLY),
54
4.96k
        mVertexBufferShadowBuffer(false),
55
4.96k
        mIndexBufferShadowBuffer(false),
56
4.96k
        mPreparedForShadowVolumes(false),
57
4.96k
        mEdgeListsBuilt(false),
58
4.96k
        mAutoBuildEdgeLists(false), // will be set to true by serializers of 1.20 and below
59
4.96k
        mSharedVertexDataAnimationType(VAT_NONE),
60
4.96k
        mSharedVertexDataAnimationIncludesNormals(false),
61
4.96k
        mAnimationTypesDirty(true),
62
4.96k
        mPosesIncludeNormals(false),
63
4.96k
        sharedVertexData(0)
64
4.96k
    {
65
        // Init first (manual) lod
66
4.96k
        MeshLodUsage lod;
67
4.96k
        lod.userValue = 0; // User value not used for base LOD level
68
4.96k
        lod.value = getLodStrategy()->getBaseValue();
69
4.96k
        mMeshLodUsageList.push_back(lod);
70
4.96k
    }
71
    //-----------------------------------------------------------------------
72
    Mesh::~Mesh()
73
4.96k
    {
74
4.96k
        if (!HardwareBufferManager::getSingletonPtr()) // LogManager might be also gone already
75
0
        {
76
0
            printf("ERROR: '%s' is being destroyed after HardwareBufferManager. This is a bug in user code.\n", mName.c_str());
77
0
            OgreAssertDbg(false,  "Mesh destroyed after HardwareBufferManager"); // assert in debug mode
78
0
            return; // try not to crash
79
0
        }
80
        // have to call this here reather than in Resource destructor
81
        // since calling virtual methods in base destructors causes crash
82
4.96k
        unload();
83
4.96k
    }
84
    //-----------------------------------------------------------------------
85
    HardwareBufferManagerBase* Mesh::getHardwareBufferManager()
86
79.6k
    {
87
79.6k
        return mBufferManager ? mBufferManager : HardwareBufferManager::getSingletonPtr();
88
79.6k
    }
89
    //-----------------------------------------------------------------------
90
    SubMesh* Mesh::createSubMesh()
91
100k
    {
92
100k
        SubMesh* sub = OGRE_NEW SubMesh();
93
100k
        sub->parent = this;
94
95
100k
        mSubMeshList.push_back(sub);
96
97
100k
        if (isLoaded())
98
100k
            _dirtyState();
99
100
100k
        return sub;
101
100k
    }
102
    //-----------------------------------------------------------------------
103
    SubMesh* Mesh::createSubMesh(const String& name)
104
0
    {
105
0
        SubMesh *sub = createSubMesh();
106
0
        nameSubMesh(name, (ushort)mSubMeshList.size()-1);
107
0
        return sub ;
108
0
    }
109
    //-----------------------------------------------------------------------
110
    void Mesh::destroySubMesh(unsigned short index)
111
0
    {
112
0
        OgreAssert(index < mSubMeshList.size(), "");
113
0
        SubMeshList::iterator i = mSubMeshList.begin();
114
0
        std::advance(i, index);
115
0
        OGRE_DELETE *i;
116
0
        mSubMeshList.erase(i);
117
118
        // Fix up any name/index entries
119
0
        for(SubMeshNameMap::iterator ni = mSubMeshNameMap.begin(); ni != mSubMeshNameMap.end();)
120
0
        {
121
0
            if (ni->second == index)
122
0
            {
123
0
                SubMeshNameMap::iterator eraseIt = ni++;
124
0
                mSubMeshNameMap.erase(eraseIt);
125
0
            }
126
0
            else
127
0
            {
128
                // reduce indexes following
129
0
                if (ni->second > index)
130
0
                    ni->second = ni->second - 1;
131
132
0
                ++ni;
133
0
            }
134
0
        }
135
136
        // fix edge list data by simply recreating all edge lists
137
0
        if( mEdgeListsBuilt)
138
0
        {
139
0
            this->freeEdgeList();
140
0
            this->buildEdgeList();
141
0
        }
142
143
0
        if (isLoaded())
144
0
            _dirtyState();
145
146
0
    }
147
    //-----------------------------------------------------------------------
148
    void Mesh::destroySubMesh(const String& name)
149
0
    {
150
0
        unsigned short index = _getSubMeshIndex(name);
151
0
        destroySubMesh(index);
152
0
    }
153
    //---------------------------------------------------------------------
154
    void Mesh::nameSubMesh(const String& name, ushort index)
155
14.8k
    {
156
14.8k
        mSubMeshNameMap[name] = index ;
157
14.8k
    }
158
159
    //---------------------------------------------------------------------
160
    void Mesh::unnameSubMesh(const String& name)
161
0
    {
162
0
        SubMeshNameMap::iterator i = mSubMeshNameMap.find(name);
163
0
        if (i != mSubMeshNameMap.end())
164
0
            mSubMeshNameMap.erase(i);
165
0
    }
166
    //-----------------------------------------------------------------------
167
    SubMesh* Mesh::getSubMesh(const String& name) const
168
0
    {
169
0
        ushort index = _getSubMeshIndex(name);
170
0
        return getSubMesh(index);
171
0
    }
172
    //-----------------------------------------------------------------------
173
    void Mesh::postLoadImpl(void)
174
4.96k
    {
175
        // Prepare for shadow volumes?
176
4.96k
        if (MeshManager::getSingleton().getPrepareAllMeshesForShadowVolumes())
177
0
        {
178
0
            if (mEdgeListsBuilt || mAutoBuildEdgeLists)
179
0
            {
180
0
                prepareForShadowVolume();
181
0
            }
182
183
0
            if (!mEdgeListsBuilt && mAutoBuildEdgeLists)
184
0
            {
185
0
                buildEdgeList();
186
0
            }
187
0
        }
188
4.96k
#if !OGRE_NO_MESHLOD
189
        // The loading process accesses LOD usages directly, so
190
        // transformation of user values must occur after loading is complete.
191
192
        // Transform user LOD values (starting at index 1, no need to transform base value)
193
4.96k
        for (auto& m : mMeshLodUsageList)
194
4.96k
            m.value = mLodStrategy->transformUserValue(m.userValue);
195
        // Rewrite first value
196
4.96k
        mMeshLodUsageList[0].value = mLodStrategy->getBaseValue();
197
4.96k
#endif
198
4.96k
    }
199
    //-----------------------------------------------------------------------
200
    void Mesh::prepareImpl()
201
0
    {
202
        // Load from specified 'name'
203
0
        if (getCreator()->getVerbose())
204
0
            LogManager::getSingleton().logMessage("Mesh: Loading "+mName+".");
205
206
0
        mFreshFromDisk =
207
0
            ResourceGroupManager::getSingleton().openResource(
208
0
                mName, mGroup, this);
209
210
        // fully prebuffer into host RAM
211
0
        mFreshFromDisk = DataStreamPtr(OGRE_NEW MemoryDataStream(mName,mFreshFromDisk));
212
0
    }
213
    //-----------------------------------------------------------------------
214
    void Mesh::unprepareImpl()
215
0
    {
216
0
        mFreshFromDisk.reset();
217
0
    }
218
    void Mesh::loadImpl()
219
0
    {
220
        // If the only copy is local on the stack, it will be cleaned
221
        // up reliably in case of exceptions, etc
222
0
        DataStreamPtr data(mFreshFromDisk);
223
0
        mFreshFromDisk.reset();
224
225
0
        if (!data) {
226
0
            OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
227
0
                        "Data doesn't appear to have been prepared in " + mName,
228
0
                        "Mesh::loadImpl()");
229
0
        }
230
231
0
        String baseName, strExt;
232
0
        StringUtil::splitBaseFilename(mName, baseName, strExt);
233
0
        auto codec = Codec::getCodec(strExt);
234
0
        if (!codec)
235
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "No codec found to load " + mName);
236
237
0
        codec->decode(data, this);
238
0
    }
239
240
    //-----------------------------------------------------------------------
241
    void Mesh::unloadImpl()
242
4.96k
    {
243
        // Teardown submeshes
244
4.96k
        for (auto & i : mSubMeshList)
245
100k
        {
246
100k
            OGRE_DELETE i;
247
100k
        }
248
4.96k
        if (sharedVertexData)
249
1.01k
        {
250
1.01k
            resetVertexData();
251
1.01k
        }
252
        // Clear SubMesh lists
253
4.96k
        mSubMeshList.clear();
254
4.96k
        mSubMeshNameMap.clear();
255
256
4.96k
        freeEdgeList();
257
4.96k
#if !OGRE_NO_MESHLOD
258
        // Removes all LOD data
259
4.96k
        removeLodLevels();
260
4.96k
#endif
261
4.96k
        mPreparedForShadowVolumes = false;
262
263
        // remove all poses & animations
264
4.96k
        removeAllAnimations();
265
4.96k
        removeAllPoses();
266
267
        // Clear bone assignments
268
4.96k
        mBoneAssignments.clear();
269
4.96k
        mBoneAssignmentsOutOfDate = false;
270
271
        // Removes reference to skeleton
272
4.96k
        mSkeleton.reset();
273
4.96k
    }
274
    //-----------------------------------------------------------------------
275
    void Mesh::reload(LoadingFlags flags)
276
0
    {
277
0
        bool wasPreparedForShadowVolumes = mPreparedForShadowVolumes;
278
0
        bool wasEdgeListsBuilt = mEdgeListsBuilt;
279
0
        bool wasAutoBuildEdgeLists = mAutoBuildEdgeLists;
280
281
0
        Resource::reload(flags);
282
283
0
        if(flags & LF_PRESERVE_STATE)
284
0
        {
285
0
            if(wasPreparedForShadowVolumes)
286
0
                prepareForShadowVolume();
287
0
            if(wasEdgeListsBuilt)
288
0
                buildEdgeList();
289
0
            setAutoBuildEdgeLists(wasAutoBuildEdgeLists);
290
0
        }
291
0
    }
292
    //-----------------------------------------------------------------------
293
    MeshPtr Mesh::clone(const String& newName, const String& newGroup)
294
0
    {
295
        // This is a bit like a copy constructor, but with the additional aspect of registering the clone with
296
        //  the MeshManager
297
298
        // New Mesh is assumed to be manually defined rather than loaded since you're cloning it for a reason
299
0
        String theGroup = newGroup.empty() ? this->getGroup() : newGroup;
300
0
        MeshPtr newMesh = MeshManager::getSingleton().createManual(newName, theGroup);
301
302
0
        if(!newMesh) // interception by collision handler
303
0
            return newMesh;
304
305
0
        newMesh->mBufferManager = mBufferManager;
306
0
        newMesh->mVertexBufferUsage = mVertexBufferUsage;
307
0
        newMesh->mIndexBufferUsage = mIndexBufferUsage;
308
0
        newMesh->mVertexBufferShadowBuffer = mVertexBufferShadowBuffer;
309
0
        newMesh->mIndexBufferShadowBuffer = mIndexBufferShadowBuffer;
310
311
        // Copy submeshes first
312
0
        for (auto *subi : mSubMeshList)
313
0
        {
314
0
            subi->clone("", newMesh.get());
315
0
        }
316
317
        // Copy shared geometry and index map, if any
318
0
        if (sharedVertexData)
319
0
        {
320
0
            newMesh->resetVertexData(sharedVertexData->clone(true, mBufferManager));
321
0
            newMesh->sharedBlendIndexToBoneIndexMap = sharedBlendIndexToBoneIndexMap;
322
0
        }
323
324
        // Copy submesh names
325
0
        newMesh->mSubMeshNameMap = mSubMeshNameMap ;
326
        // Copy any bone assignments
327
0
        newMesh->mBoneAssignments = mBoneAssignments;
328
0
        newMesh->mBoneAssignmentsOutOfDate = mBoneAssignmentsOutOfDate;
329
        // Copy bounds
330
0
        newMesh->mAABB = mAABB;
331
0
        newMesh->mBoundRadius = mBoundRadius;
332
0
        newMesh->mBoneBoundingRadius = mBoneBoundingRadius;
333
0
        newMesh->mAutoBuildEdgeLists = mAutoBuildEdgeLists;
334
0
        newMesh->mEdgeListsBuilt = mEdgeListsBuilt;
335
336
0
#if !OGRE_NO_MESHLOD
337
0
        newMesh->mHasManualLodLevel = mHasManualLodLevel;
338
0
        newMesh->mLodStrategy = mLodStrategy;
339
0
        newMesh->mMeshLodUsageList = mMeshLodUsageList;
340
0
#endif
341
        // Unreference edge lists, otherwise we'll delete the same lot twice, build on demand
342
0
        MeshLodUsageList::iterator lodOldi;
343
0
        lodOldi = mMeshLodUsageList.begin();
344
0
        for (auto& lodi : newMesh->mMeshLodUsageList) {
345
0
            MeshLodUsage& lod = *lodOldi;
346
0
            lodi.manualName = lod.manualName;
347
0
            lodi.userValue = lod.userValue;
348
0
            lodi.value = lod.value;
349
0
            if (lod.edgeData) {
350
0
                lodi.edgeData = lod.edgeData->clone();
351
0
            }
352
0
            ++lodOldi;
353
0
        }
354
355
0
        newMesh->mSkeleton = mSkeleton;
356
357
        // Keep prepared shadow volume info (buffers may already be prepared)
358
0
        newMesh->mPreparedForShadowVolumes = mPreparedForShadowVolumes;
359
360
0
        newMesh->mEdgeListsBuilt = mEdgeListsBuilt;
361
362
        // Clone vertex animation
363
0
        for (auto & i : mAnimationsList)
364
0
        {
365
0
            Animation *newAnim = i.second->clone(i.second->getName());
366
0
            newMesh->mAnimationsList[i.second->getName()] = newAnim;
367
0
        }
368
        // Clone pose list
369
0
        for (auto & i : mPoseList)
370
0
        {
371
0
            Pose* newPose = i->clone();
372
0
            newMesh->mPoseList.push_back(newPose);
373
0
        }
374
0
        newMesh->mSharedVertexDataAnimationType = mSharedVertexDataAnimationType;
375
0
        newMesh->mAnimationTypesDirty = true;
376
377
0
        newMesh->load();
378
0
        newMesh->touch();
379
380
0
        return newMesh;
381
0
    }
382
    //-----------------------------------------------------------------------
383
    const AxisAlignedBox& Mesh::getBounds(void) const
384
0
    {
385
0
        return mAABB;
386
0
    }
387
    //-----------------------------------------------------------------------
388
    void Mesh::_setBounds(const AxisAlignedBox& bounds, bool pad)
389
4.97k
    {
390
4.97k
        mAABB = bounds;
391
4.97k
        mBoundRadius = Math::boundingRadiusFromAABB(mAABB);
392
393
4.97k
        if( mAABB.isFinite() )
394
4.97k
        {
395
4.97k
            Vector3 max = mAABB.getMaximum();
396
4.97k
            Vector3 min = mAABB.getMinimum();
397
398
4.97k
            if (pad)
399
0
            {
400
                // Pad out the AABB a little, helps with most bounds tests
401
0
                Vector3 scaler = (max - min) * MeshManager::getSingleton().getBoundsPaddingFactor();
402
0
                mAABB.setExtents(min  - scaler, max + scaler);
403
                // Pad out the sphere a little too
404
0
                mBoundRadius = mBoundRadius + (mBoundRadius * MeshManager::getSingleton().getBoundsPaddingFactor());
405
0
            }
406
4.97k
        }
407
4.97k
    }
408
    //-----------------------------------------------------------------------
409
    void Mesh::_setBoundingSphereRadius(Real radius)
410
4.97k
    {
411
4.97k
        mBoundRadius = radius;
412
4.97k
    }
413
    //-----------------------------------------------------------------------
414
    void Mesh::_setBoneBoundingRadius(Real radius)
415
0
    {
416
0
        mBoneBoundingRadius = radius;
417
0
    }
418
    //-----------------------------------------------------------------------
419
    void Mesh::_updateBoundsFromVertexBuffers(bool pad)
420
0
    {
421
0
        bool extendOnly = false; // First time we need full AABB of the given submesh, but on the second call just extend that one.
422
0
        if (sharedVertexData){
423
0
            _calcBoundsFromVertexBuffer(sharedVertexData, mAABB, mBoundRadius, extendOnly);
424
0
            extendOnly = true;
425
0
        }
426
0
        for (auto & i : mSubMeshList){
427
0
            if (i->vertexData){
428
0
                _calcBoundsFromVertexBuffer(i->vertexData, mAABB, mBoundRadius, extendOnly);
429
0
                extendOnly = true;
430
0
            }
431
0
        }
432
0
        if (pad)
433
0
        {
434
0
            Vector3 max = mAABB.getMaximum();
435
0
            Vector3 min = mAABB.getMinimum();
436
            // Pad out the AABB a little, helps with most bounds tests
437
0
            Vector3 scaler = (max - min) * MeshManager::getSingleton().getBoundsPaddingFactor();
438
0
            mAABB.setExtents(min - scaler, max + scaler);
439
            // Pad out the sphere a little too
440
0
            mBoundRadius = mBoundRadius + (mBoundRadius * MeshManager::getSingleton().getBoundsPaddingFactor());
441
0
        }
442
0
    }
443
    void Mesh::_calcBoundsFromVertexBuffer(VertexData* vertexData, AxisAlignedBox& outAABB, Real& outRadius, bool extendOnly /*= false*/)
444
0
    {
445
0
        if (vertexData->vertexCount == 0) {
446
0
            if (!extendOnly) {
447
0
                outAABB = AxisAlignedBox(Vector3::ZERO, Vector3::ZERO);
448
0
                outRadius = 0;
449
0
            }
450
0
            return;
451
0
        }
452
0
        const VertexElement* elemPos = vertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
453
0
        HardwareVertexBufferSharedPtr vbuf = vertexData->vertexBufferBinding->getBuffer(elemPos->getSource());
454
0
        HardwareBufferLockGuard vertexLock(vbuf, HardwareBuffer::HBL_READ_ONLY);
455
0
        unsigned char* vertex = static_cast<unsigned char*>(vertexLock.pData);
456
457
0
        if (!extendOnly){
458
            // init values
459
0
            outRadius = 0;
460
0
            float* pFloat;
461
0
            elemPos->baseVertexPointerToElement(vertex, &pFloat);
462
0
            Vector3 basePos(pFloat[0], pFloat[1], pFloat[2]);
463
0
            outAABB.setExtents(basePos, basePos);
464
0
        }
465
0
        size_t vSize = vbuf->getVertexSize();
466
0
        unsigned char* vEnd = vertex + vertexData->vertexCount * vSize;
467
0
        Real radiusSqr = outRadius * outRadius;
468
        // Loop through all vertices.
469
0
        for (; vertex < vEnd; vertex += vSize) {
470
0
            float* pFloat;
471
0
            elemPos->baseVertexPointerToElement(vertex, &pFloat);
472
0
            Vector3 pos(pFloat[0], pFloat[1], pFloat[2]);
473
0
            outAABB.getMinimum().makeFloor(pos);
474
0
            outAABB.getMaximum().makeCeil(pos);
475
0
            radiusSqr = std::max<Real>(radiusSqr, pos.squaredLength());
476
0
        }
477
0
        outRadius = std::sqrt(radiusSqr);
478
0
    }
479
    //-----------------------------------------------------------------------
480
    void Mesh::setSkeletonName(const String& skelName)
481
21.8k
    {
482
21.8k
        if (skelName != getSkeletonName())
483
21.4k
        {
484
21.4k
            if (skelName.empty())
485
0
            {
486
                // No skeleton
487
0
                mSkeleton.reset();
488
0
            }
489
21.4k
            else
490
21.4k
            {
491
                // Load skeleton
492
21.4k
                try {
493
21.4k
                    mSkeleton = static_pointer_cast<Skeleton>(SkeletonManager::getSingleton().load(skelName, mGroup));
494
21.4k
                }
495
21.4k
                catch (...)
496
21.4k
                {
497
21.4k
                    mSkeleton.reset();
498
                    // Log this error
499
21.4k
                    String msg = "Unable to load skeleton '";
500
21.4k
                    msg += skelName + "' for Mesh '" + mName + "'. This Mesh will not be animated.";
501
21.4k
                    LogManager::getSingleton().logError(msg);
502
503
21.4k
                }
504
505
506
21.4k
            }
507
21.4k
            if (isLoaded())
508
21.4k
                _dirtyState();
509
21.4k
        }
510
21.8k
    }
511
    //-----------------------------------------------------------------------
512
    void Mesh::addBoneAssignment(const VertexBoneAssignment& vertBoneAssign)
513
2.16k
    {
514
2.16k
        mBoneAssignments.emplace(vertBoneAssign.vertexIndex, vertBoneAssign);
515
2.16k
        mBoneAssignmentsOutOfDate = true;
516
2.16k
    }
517
    //-----------------------------------------------------------------------
518
    void Mesh::clearBoneAssignments(void)
519
0
    {
520
0
        mBoneAssignments.clear();
521
0
        mBoneAssignmentsOutOfDate = true;
522
0
    }
523
    //-----------------------------------------------------------------------
524
    void Mesh::_initAnimationState(AnimationStateSet* animSet)
525
0
    {
526
        // Animation states for skeletal animation
527
0
        if (mSkeleton)
528
0
        {
529
            // Delegate to Skeleton
530
0
            mSkeleton->_initAnimationState(animSet);
531
532
            // Take the opportunity to update the compiled bone assignments
533
0
            _updateCompiledBoneAssignments();
534
535
0
        }
536
537
        // Animation states for vertex animation
538
0
        for (auto & i : mAnimationsList)
539
0
        {
540
            // Only create a new animation state if it doesn't exist
541
            // We can have the same named animation in both skeletal and vertex
542
            // with a shared animation state affecting both, for combined effects
543
            // The animations should be the same length if this feature is used!
544
0
            if (!animSet->hasAnimationState(i.second->getName()))
545
0
            {
546
0
                animSet->createAnimationState(i.second->getName(), 0.0,
547
0
                    i.second->getLength());
548
0
            }
549
550
0
        }
551
552
0
    }
553
    //---------------------------------------------------------------------
554
    void Mesh::_refreshAnimationState(AnimationStateSet* animSet)
555
0
    {
556
0
        if (mSkeleton)
557
0
        {
558
0
            mSkeleton->_refreshAnimationState(animSet);
559
0
        }
560
561
        // Merge in any new vertex animations
562
0
        for (auto& i : mAnimationsList)
563
0
        {
564
            // Create animation at time index 0, default params mean this has weight 1 and is disabled
565
0
            auto anim = i.second;
566
0
            const String& animName = anim->getName();
567
0
            if (!animSet->hasAnimationState(animName))
568
0
            {
569
0
                animSet->createAnimationState(animName, 0.0, anim->getLength());
570
0
            }
571
0
            else
572
0
            {
573
                // Update length incase changed
574
0
                AnimationState* animState = animSet->getAnimationState(animName);
575
0
                animState->setLength(anim->getLength());
576
0
                animState->setTimePosition(std::min(anim->getLength(), animState->getTimePosition()));
577
0
            }
578
0
        }
579
0
    }
580
    //-----------------------------------------------------------------------
581
    void Mesh::_updateCompiledBoneAssignments(void)
582
0
    {
583
0
        if (mBoneAssignmentsOutOfDate)
584
0
            _compileBoneAssignments();
585
586
0
        for (auto *m : mSubMeshList)
587
0
        {
588
0
            if (m->mBoneAssignmentsOutOfDate)
589
0
            {
590
0
                m->_compileBoneAssignments();
591
0
            }
592
0
        }
593
0
    }
594
    //-----------------------------------------------------------------------
595
    typedef std::multimap<Real, Mesh::VertexBoneAssignmentList::iterator> WeightIteratorMap;
596
    unsigned short Mesh::_rationaliseBoneAssignments(size_t vertexCount, Mesh::VertexBoneAssignmentList& assignments)
597
0
    {
598
        // Iterate through, finding the largest # bones per vertex
599
0
        unsigned short maxBones = 0;
600
0
        bool existsNonSkinnedVertices = false;
601
0
        VertexBoneAssignmentList::iterator i;
602
603
0
        for (size_t v = 0; v < vertexCount; ++v)
604
0
        {
605
            // Get number of entries for this vertex
606
0
            short currBones = static_cast<unsigned short>(assignments.count(v));
607
0
            if (currBones <= 0)
608
0
                existsNonSkinnedVertices = true;
609
610
            // Deal with max bones update
611
            // (note this will record maxBones even if they exceed limit)
612
0
            if (maxBones < currBones)
613
0
                maxBones = currBones;
614
            // does the number of bone assignments exceed limit?
615
0
            if (currBones > OGRE_MAX_BLEND_WEIGHTS)
616
0
            {
617
                // To many bone assignments on this vertex
618
                // Find the start & end (end is in iterator terms ie exclusive)
619
0
                std::pair<VertexBoneAssignmentList::iterator, VertexBoneAssignmentList::iterator> range;
620
                // map to sort by weight
621
0
                WeightIteratorMap weightToAssignmentMap;
622
0
                range = assignments.equal_range(v);
623
                // Add all the assignments to map
624
0
                for (i = range.first; i != range.second; ++i)
625
0
                {
626
                    // insert value weight->iterator
627
0
                    weightToAssignmentMap.emplace(i->second.weight, i);
628
0
                }
629
                // Reverse iterate over weight map, remove lowest n
630
0
                unsigned short numToRemove = currBones - OGRE_MAX_BLEND_WEIGHTS;
631
0
                WeightIteratorMap::iterator remIt = weightToAssignmentMap.begin();
632
633
0
                while (numToRemove--)
634
0
                {
635
                    // Erase this one
636
0
                    assignments.erase(remIt->second);
637
0
                    ++remIt;
638
0
                }
639
0
            } // if (currBones > OGRE_MAX_BLEND_WEIGHTS)
640
641
            // Make sure the weights are normalised
642
            // Do this irrespective of whether we had to remove assignments or not
643
            //   since it gives us a guarantee that weights are normalised
644
            //  We assume this, so it's a good idea since some modellers may not
645
0
            std::pair<VertexBoneAssignmentList::iterator, VertexBoneAssignmentList::iterator> normalise_range = assignments.equal_range(v);
646
0
            Real totalWeight = 0;
647
            // Find total first
648
0
            for (i = normalise_range.first; i != normalise_range.second; ++i)
649
0
            {
650
0
                totalWeight += i->second.weight;
651
0
            }
652
            // Now normalise if total weight is outside tolerance
653
0
            if (!Math::RealEqual(totalWeight, 1.0f))
654
0
            {
655
0
                for (i = normalise_range.first; i != normalise_range.second; ++i)
656
0
                {
657
0
                    i->second.weight = i->second.weight / totalWeight;
658
0
                }
659
0
            }
660
661
0
        }
662
663
0
        if (maxBones > OGRE_MAX_BLEND_WEIGHTS)
664
0
        {
665
            // Warn that we've reduced bone assignments
666
0
            LogManager::getSingleton().logWarning("the mesh '" + mName + "' "
667
0
                "includes vertices with more than " +
668
0
                StringConverter::toString(OGRE_MAX_BLEND_WEIGHTS) + " bone assignments. "
669
0
                "The lowest weighted assignments beyond this limit have been removed, so "
670
0
                "your animation may look slightly different. To eliminate this, reduce "
671
0
                "the number of bone assignments per vertex on your mesh to " +
672
0
                StringConverter::toString(OGRE_MAX_BLEND_WEIGHTS) + ".");
673
            // we've adjusted them down to the max
674
0
            maxBones = OGRE_MAX_BLEND_WEIGHTS;
675
676
0
        }
677
678
0
        if (existsNonSkinnedVertices)
679
0
        {
680
            // Warn that we've non-skinned vertices
681
0
            LogManager::getSingleton().logWarning("the mesh '" + mName + "' "
682
0
                "includes vertices without bone assignments. Those vertices will "
683
0
                "transform to wrong position when skeletal animation enabled. "
684
0
                "To eliminate this, assign at least one bone assignment per vertex "
685
0
                "on your mesh.");
686
0
        }
687
688
0
        return maxBones;
689
0
    }
690
    //-----------------------------------------------------------------------
691
    void  Mesh::_compileBoneAssignments(void)
692
0
    {
693
0
        if (sharedVertexData)
694
0
        {
695
0
            unsigned short maxBones = _rationaliseBoneAssignments(sharedVertexData->vertexCount, mBoneAssignments);
696
697
0
            if (maxBones != 0)
698
0
            {
699
0
                compileBoneAssignments(mBoneAssignments, maxBones,
700
0
                    sharedBlendIndexToBoneIndexMap, sharedVertexData);
701
0
            }
702
0
        }
703
0
        mBoneAssignmentsOutOfDate = false;
704
0
    }
705
    //---------------------------------------------------------------------
706
    void Mesh::buildIndexMap(const VertexBoneAssignmentList& boneAssignments,
707
        IndexMap& boneIndexToBlendIndexMap, IndexMap& blendIndexToBoneIndexMap)
708
0
    {
709
0
        if (boneAssignments.empty())
710
0
        {
711
            // Just in case
712
0
            boneIndexToBlendIndexMap.clear();
713
0
            blendIndexToBoneIndexMap.clear();
714
0
            return;
715
0
        }
716
717
0
        typedef std::set<unsigned short> BoneIndexSet;
718
0
        BoneIndexSet usedBoneIndices;
719
720
        // Collect actually used bones
721
0
        for (auto& itVBA : boneAssignments)
722
0
        {
723
0
            usedBoneIndices.insert(itVBA.second.boneIndex);
724
0
        }
725
726
        // Allocate space for index map
727
0
        blendIndexToBoneIndexMap.resize(usedBoneIndices.size());
728
0
        boneIndexToBlendIndexMap.resize(*usedBoneIndices.rbegin() + 1);
729
730
        // Make index map between bone index and blend index
731
0
        unsigned short blendIndex = 0;
732
0
        for (auto& itBoneIndex : usedBoneIndices)
733
0
        {
734
0
            boneIndexToBlendIndexMap[itBoneIndex] = blendIndex;
735
0
            blendIndexToBoneIndexMap[blendIndex] = itBoneIndex;
736
0
            ++blendIndex;
737
0
        }
738
0
    }
739
    //---------------------------------------------------------------------
740
    void Mesh::compileBoneAssignments(
741
        const VertexBoneAssignmentList& boneAssignments,
742
        unsigned short numBlendWeightsPerVertex,
743
        IndexMap& blendIndexToBoneIndexMap,
744
        VertexData* targetVertexData)
745
0
    {
746
        // Create or reuse blend weight / indexes buffer
747
        // Indices are always a UBYTE4 no matter how many weights per vertex
748
0
        VertexDeclaration* decl = targetVertexData->vertexDeclaration;
749
0
        VertexBufferBinding* bind = targetVertexData->vertexBufferBinding;
750
0
        unsigned short bindIndex;
751
752
        // Build the index map brute-force. It's possible to store the index map
753
        // in .mesh, but maybe trivial.
754
0
        IndexMap boneIndexToBlendIndexMap;
755
0
        buildIndexMap(boneAssignments, boneIndexToBlendIndexMap, blendIndexToBoneIndexMap);
756
757
0
        const VertexElement* testElem =
758
0
            decl->findElementBySemantic(VES_BLEND_INDICES);
759
0
        if (testElem)
760
0
        {
761
            // Already have a buffer, unset it & delete elements
762
0
            bindIndex = testElem->getSource();
763
            // unset will cause deletion of buffer
764
0
            bind->unsetBinding(bindIndex);
765
0
            decl->removeElement(VES_BLEND_INDICES);
766
0
            decl->removeElement(VES_BLEND_WEIGHTS);
767
0
        }
768
0
        else
769
0
        {
770
            // Get new binding
771
0
            bindIndex = bind->getNextIndex();
772
0
        }
773
        // type of Weights is settable on the MeshManager.
774
0
        VertexElementType weightsBaseType = MeshManager::getSingleton().getBlendWeightsBaseElementType();
775
0
        VertexElementType weightsVertexElemType = VertexElement::multiplyTypeCount( weightsBaseType, numBlendWeightsPerVertex );
776
0
        HardwareVertexBufferSharedPtr vbuf = getHardwareBufferManager()->createVertexBuffer(
777
0
            sizeof( unsigned char ) * 4 + VertexElement::getTypeSize( weightsVertexElemType ),
778
0
                targetVertexData->vertexCount,
779
0
                HardwareBuffer::HBU_STATIC_WRITE_ONLY,
780
0
                true // use shadow buffer
781
0
                );
782
        // bind new buffer
783
0
        bind->setBinding(bindIndex, vbuf);
784
0
        const VertexElement *pIdxElem, *pWeightElem;
785
786
        // add new vertex elements
787
        // Note, insert directly after all elements using the same source as
788
        // position to abide by pre-Dx9 format restrictions
789
0
        const VertexElement* firstElem = decl->getElement(0);
790
0
        if(firstElem->getSemantic() == VES_POSITION)
791
0
        {
792
0
            unsigned short insertPoint = 1;
793
0
            while (insertPoint < decl->getElementCount() &&
794
0
                decl->getElement(insertPoint)->getSource() == firstElem->getSource())
795
0
            {
796
0
                ++insertPoint;
797
0
            }
798
0
            const VertexElement& idxElem =
799
0
                decl->insertElement(insertPoint, bindIndex, 0, VET_UBYTE4, VES_BLEND_INDICES);
800
0
            const VertexElement& wtElem =
801
0
                decl->insertElement(insertPoint+1, bindIndex, sizeof(unsigned char)*4, weightsVertexElemType, VES_BLEND_WEIGHTS);
802
0
            pIdxElem = &idxElem;
803
0
            pWeightElem = &wtElem;
804
0
        }
805
0
        else
806
0
        {
807
            // Position is not the first semantic, therefore this declaration is
808
            // not pre-Dx9 compatible anyway, so just tack it on the end
809
0
            const VertexElement& idxElem =
810
0
                decl->addElement(bindIndex, 0, VET_UBYTE4, VES_BLEND_INDICES);
811
0
            const VertexElement& wtElem =
812
0
                decl->addElement(bindIndex, sizeof(unsigned char)*4, weightsVertexElemType, VES_BLEND_WEIGHTS );
813
0
            pIdxElem = &idxElem;
814
0
            pWeightElem = &wtElem;
815
0
        }
816
817
0
        unsigned int maxIntWt = 0;
818
        // keeping a switch out of the loop
819
0
        switch ( weightsBaseType )
820
0
        {
821
0
            default:
822
0
              OgreAssert(false, "Invalid BlendWeightsBaseElementType");
823
0
              break;
824
0
            case VET_FLOAT1:
825
0
                break;
826
0
            case VET_UBYTE4_NORM:
827
0
                maxIntWt = 0xff;
828
0
                break;
829
0
            case VET_USHORT2_NORM:
830
0
                maxIntWt = 0xffff;
831
0
                break;
832
0
            case VET_SHORT2_NORM:
833
0
                maxIntWt = 0x7fff;
834
0
                break;
835
0
        }
836
        // Assign data
837
0
        size_t v;
838
0
        VertexBoneAssignmentList::const_iterator i, iend;
839
0
        i = boneAssignments.begin();
840
0
        iend = boneAssignments.end();
841
0
        HardwareBufferLockGuard vertexLock(vbuf, HardwareBuffer::HBL_DISCARD);
842
0
        unsigned char *pBase = static_cast<unsigned char*>(vertexLock.pData);
843
        // Iterate by vertex
844
0
        for (v = 0; v < targetVertexData->vertexCount; ++v)
845
0
        {
846
            // collect the indices/weights in these arrays
847
0
            unsigned char indices[ 4 ] = { 0, 0, 0, 0 };
848
0
            float weights[ 4 ] = { 1.0f, 0.0f, 0.0f, 0.0f };
849
0
            for (unsigned short bone = 0; bone < numBlendWeightsPerVertex; ++bone)
850
0
            {
851
                // Do we still have data for this vertex?
852
0
                if (i != iend && i->second.vertexIndex == v)
853
0
                {
854
                    // If so, grab weight and index
855
0
                    weights[ bone ] = i->second.weight;
856
0
                    indices[ bone ] = static_cast<unsigned char>( boneIndexToBlendIndexMap[ i->second.boneIndex ] );
857
0
                    ++i;
858
0
                }
859
0
            }
860
            // if weights are integers,
861
0
            if ( weightsBaseType != VET_FLOAT1 )
862
0
            {
863
                // pack the float weights into shorts/bytes
864
0
                unsigned int intWeights[ 4 ];
865
0
                unsigned int sum = 0;
866
0
                const unsigned int wtScale = maxIntWt;  // this value corresponds to a weight of 1.0
867
0
                for ( int ii = 0; ii < 4; ++ii )
868
0
                {
869
0
                    unsigned int bw = static_cast<unsigned int>( weights[ ii ] * wtScale );
870
0
                    intWeights[ ii ] = bw;
871
0
                    sum += bw;
872
0
                }
873
                // if the sum doesn't add up due to roundoff error, we need to adjust the intWeights so that the sum is wtScale
874
0
                if ( sum != maxIntWt )
875
0
                {
876
                    // find the largest weight (it isn't necessarily the first one...)
877
0
                    int iMaxWeight = 0;
878
0
                    unsigned int maxWeight = 0;
879
0
                    for ( int ii = 0; ii < 4; ++ii )
880
0
                    {
881
0
                        unsigned int bw = intWeights[ ii ];
882
0
                        if ( bw > maxWeight )
883
0
                        {
884
0
                            iMaxWeight = ii;
885
0
                            maxWeight = bw;
886
0
                        }
887
0
                    }
888
                    // Adjust the largest weight to make sure the sum is correct.
889
                    // The idea is that changing the largest weight will have the smallest effect
890
                    // on the ratio of weights.  This works best when there is one dominant weight,
891
                    // and worst when 2 or more weights are similar in magnitude.
892
                    // A better method could be used to reduce the quantization error, but this is
893
                    // being done at run-time so it needs to be quick.
894
0
                    intWeights[ iMaxWeight ] += maxIntWt - sum;
895
0
                }
896
897
                // now write the weights
898
0
                if ( weightsBaseType == VET_UBYTE4_NORM )
899
0
                {
900
                    // write out the weights as bytes
901
0
                    unsigned char* pWeight;
902
0
                    pWeightElem->baseVertexPointerToElement( pBase, &pWeight );
903
                    // NOTE: always writes out 4 regardless of numBlendWeightsPerVertex
904
0
                    for (unsigned int intWeight : intWeights)
905
0
                    {
906
0
                        *pWeight++ = static_cast<unsigned char>( intWeight );
907
0
                    }
908
0
                }
909
0
                else
910
0
                {
911
                    // write out the weights as shorts
912
0
                    unsigned short* pWeight;
913
0
                    pWeightElem->baseVertexPointerToElement( pBase, &pWeight );
914
0
                    for ( int ii = 0; ii < numBlendWeightsPerVertex; ++ii )
915
0
                    {
916
0
                        *pWeight++ = static_cast<unsigned short>( intWeights[ ii ] );
917
0
                    }
918
0
                }
919
0
            }
920
0
            else
921
0
            {
922
                // write out the weights as floats
923
0
                float* pWeight;
924
0
                pWeightElem->baseVertexPointerToElement( pBase, &pWeight );
925
0
                for ( int ii = 0; ii < numBlendWeightsPerVertex; ++ii )
926
0
                {
927
0
                    *pWeight++ = weights[ ii ];
928
0
                }
929
0
            }
930
0
            unsigned char* pIndex;
931
0
            pIdxElem->baseVertexPointerToElement( pBase, &pIndex );
932
0
            for (unsigned char indice : indices)
933
0
            {
934
0
                *pIndex++ = indice;
935
0
            }
936
0
            pBase += vbuf->getVertexSize();
937
0
        }
938
0
    }
939
    //---------------------------------------------------------------------
940
    static Real distLineSegToPoint( const Vector3& line0, const Vector3& line1, const Vector3& pt )
941
0
    {
942
0
        Vector3 v01 = line1 - line0;
943
0
        Real tt = v01.dotProduct( pt - line0 ) / std::max( v01.dotProduct(v01), std::numeric_limits<Real>::epsilon() );
944
0
        tt = Math::Clamp( tt, Real(0.0f), Real(1.0f) );
945
0
        Vector3 onLine = line0 + tt * v01;
946
0
        return pt.distance( onLine );
947
0
    }
948
    //---------------------------------------------------------------------
949
    static Real _computeBoneBoundingRadiusHelper( VertexData* vertexData,
950
        const Mesh::VertexBoneAssignmentList& boneAssignments,
951
        const std::vector<Vector3>& bonePositions,
952
        const std::vector< std::vector<ushort> >& boneChildren
953
        )
954
0
    {
955
0
        std::vector<Vector3> vertexPositions;
956
0
        {
957
            // extract vertex positions
958
0
            const VertexElement* posElem = vertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
959
0
            HardwareVertexBufferSharedPtr vbuf = vertexData->vertexBufferBinding->getBuffer(posElem->getSource());
960
            // if usage is write only,
961
0
            if ( !vbuf->hasShadowBuffer() && (vbuf->getUsage() & HBU_DETAIL_WRITE_ONLY) )
962
0
            {
963
                // we can do it!
964
0
                DefaultHardwareBufferManagerBase bfrMgr;
965
0
                HardwareVertexBufferPtr shadowBuffer = bfrMgr.createVertexBuffer(vbuf->getVertexSize(), vbuf->getNumVertices(), Ogre::HBU_CPU_ONLY);
966
0
                shadowBuffer->copyData(*vbuf);
967
0
                vbuf = shadowBuffer;
968
0
            }
969
0
            vertexPositions.resize( vertexData->vertexCount );
970
0
            HardwareBufferLockGuard vertexLock(vbuf, HardwareBuffer::HBL_READ_ONLY);
971
0
            unsigned char* vertex = static_cast<unsigned char*>(vertexLock.pData);
972
0
            float* pFloat;
973
974
0
            for(size_t i = 0; i < vertexData->vertexCount; ++i)
975
0
            {
976
0
                posElem->baseVertexPointerToElement(vertex, &pFloat);
977
0
                vertexPositions[ i ] = Vector3( pFloat[0], pFloat[1], pFloat[2] );
978
0
                vertex += vbuf->getVertexSize();
979
0
            }
980
0
        }
981
0
        Real maxRadius = Real(0);
982
0
        Real minWeight = Real(0.01);
983
        // for each vertex-bone assignment,
984
0
        for (const auto & boneAssignment : boneAssignments)
985
0
        {
986
            // if weight is close to zero, ignore
987
0
            if (boneAssignment.second.weight > minWeight)
988
0
            {
989
                // if we have a bounding box around all bone origins, we consider how far outside this box the
990
                // current vertex could ever get (assuming it is only attached to the given bone, and the bones all have unity scale)
991
0
                size_t iBone = boneAssignment.second.boneIndex;
992
0
                const Vector3& v = vertexPositions[ boneAssignment.second.vertexIndex ];
993
0
                Vector3 diff = v - bonePositions[ iBone ];
994
0
                Real dist = diff.length();  // max distance of vertex v outside of bounding box
995
                // if this bone has children, we can reduce the dist under the assumption that the children may rotate wrt their parent, but don't translate
996
0
                for (size_t iChild = 0; iChild < boneChildren[iBone].size(); ++iChild)
997
0
                {
998
                    // given this assumption, we know that the bounding box will enclose both the bone origin as well as the origin of the child bone,
999
                    // and therefore everything on a line segment between the bone origin and the child bone will be inside the bounding box as well
1000
0
                    size_t iChildBone = boneChildren[ iBone ][ iChild ];
1001
                    // compute distance from vertex to line segment between bones
1002
0
                    Real distChild = distLineSegToPoint( bonePositions[ iBone ], bonePositions[ iChildBone ], v );
1003
0
                    dist = std::min( dist, distChild );
1004
0
                }
1005
                // scale the distance by the weight, this prevents the radius from being over-inflated because of a vertex that is lightly influenced by a faraway bone
1006
0
                dist *= boneAssignment.second.weight;
1007
0
                maxRadius = std::max( maxRadius, dist );
1008
0
            }
1009
0
        }
1010
0
        return maxRadius;
1011
0
    }
1012
    //---------------------------------------------------------------------
1013
    void Mesh::_computeBoneBoundingRadius()
1014
0
    {
1015
0
        if (mBoneBoundingRadius == Real(0) && mSkeleton)
1016
0
        {
1017
0
            Real radius = Real(0);
1018
0
            std::vector<Vector3> bonePositions;
1019
0
            std::vector< std::vector<ushort> > boneChildren;  // for each bone, a list of children
1020
0
            {
1021
                // extract binding pose bone positions, and also indices for child bones
1022
0
                uint16 numBones = mSkeleton->getNumBones();
1023
0
                mSkeleton->setBindingPose();
1024
0
                mSkeleton->_updateTransforms();
1025
0
                bonePositions.resize( numBones );
1026
0
                boneChildren.resize( numBones );
1027
                // for each bone,
1028
0
                for (uint16 iBone = 0; iBone < numBones; ++iBone)
1029
0
                {
1030
0
                    Bone* bone = mSkeleton->getBone( iBone );
1031
0
                    bonePositions[ iBone ] = bone->_getDerivedPosition();
1032
0
                    boneChildren[ iBone ].reserve( bone->numChildren() );
1033
0
                    for (uint16 iChild = 0; iChild < bone->numChildren(); ++iChild)
1034
0
                    {
1035
0
                        Bone* child = static_cast<Bone*>( bone->getChild( iChild ) );
1036
0
                        boneChildren[ iBone ].push_back( child->getHandle() );
1037
0
                    }
1038
0
                }
1039
0
            }
1040
0
            if (sharedVertexData)
1041
0
            {
1042
                // check shared vertices
1043
0
                radius = _computeBoneBoundingRadiusHelper(sharedVertexData, mBoneAssignments, bonePositions, boneChildren);
1044
0
            }
1045
1046
            // check submesh vertices
1047
0
            for(auto *submesh : mSubMeshList)
1048
0
            {
1049
0
                if (!submesh->useSharedVertices && submesh->vertexData)
1050
0
                {
1051
0
                    Real r = _computeBoneBoundingRadiusHelper(submesh->vertexData, submesh->mBoneAssignments, bonePositions, boneChildren);
1052
0
                    radius = std::max( radius, r );
1053
0
                }
1054
0
            }
1055
0
            if (radius > Real(0))
1056
0
            {
1057
0
                mBoneBoundingRadius = radius;
1058
0
            }
1059
0
            else
1060
0
            {
1061
                // fallback if we failed to find the vertices
1062
0
                mBoneBoundingRadius = mBoundRadius;
1063
0
            }
1064
0
        }
1065
0
    }
1066
    //---------------------------------------------------------------------
1067
    void Mesh::_notifySkeleton(const SkeletonPtr& pSkel)
1068
0
    {
1069
0
        mSkeleton = pSkel;
1070
0
    }
1071
    //---------------------------------------------------------------------
1072
    Mesh::BoneAssignmentIterator Mesh::getBoneAssignmentIterator(void)
1073
0
    {
1074
0
        return BoneAssignmentIterator(mBoneAssignments.begin(),
1075
0
            mBoneAssignments.end());
1076
0
    }
1077
    //---------------------------------------------------------------------
1078
    const String& Mesh::getSkeletonName(void) const
1079
21.8k
    {
1080
21.8k
        return mSkeleton ? mSkeleton->getName() : BLANKSTRING;
1081
21.8k
    }
1082
    //---------------------------------------------------------------------
1083
    const MeshLodUsage& Mesh::getLodLevel(ushort index) const
1084
0
    {
1085
0
#if !OGRE_NO_MESHLOD
1086
0
        index = std::min(index, (ushort)(mMeshLodUsageList.size() - 1));
1087
0
        if (this->_isManualLodLevel(index) && index > 0 && !mMeshLodUsageList[index].manualMesh)
1088
0
        {
1089
            // Load the mesh now
1090
0
            try {
1091
0
                mMeshLodUsageList[index].manualMesh =
1092
0
                    MeshManager::getSingleton().load(
1093
0
                        mMeshLodUsageList[index].manualName,
1094
0
                        getGroup());
1095
                // get the edge data, if required
1096
0
                if (!mMeshLodUsageList[index].edgeData)
1097
0
                {
1098
0
                    mMeshLodUsageList[index].edgeData =
1099
0
                        mMeshLodUsageList[index].manualMesh->getEdgeList(0);
1100
0
                }
1101
0
            }
1102
0
            catch (Exception& )
1103
0
            {
1104
0
                LogManager::getSingleton().stream()
1105
0
                    << "Error while loading manual LOD level "
1106
0
                    << mMeshLodUsageList[index].manualName
1107
0
                    << " - this LOD level will not be rendered. You can "
1108
0
                    << "ignore this error in offline mesh tools.";
1109
0
            }
1110
1111
0
        }
1112
0
        return mMeshLodUsageList[index];
1113
#else
1114
        return mMeshLodUsageList[0];
1115
#endif
1116
0
    }
1117
    //---------------------------------------------------------------------
1118
    ushort Mesh::getLodIndex(Real value) const
1119
0
    {
1120
0
#if !OGRE_NO_MESHLOD
1121
        // Get index from strategy
1122
0
        return mLodStrategy->getIndex(value, mMeshLodUsageList);
1123
#else
1124
        return 0;
1125
#endif
1126
0
    }
1127
    //---------------------------------------------------------------------
1128
#if !OGRE_NO_MESHLOD
1129
    void Mesh::updateManualLodLevel(ushort index, const String& meshName)
1130
0
    {
1131
1132
        // Basic prerequisites
1133
0
        assert(index != 0 && "Can't modify first LOD level (full detail)");
1134
0
        assert(index < mMeshLodUsageList.size() && "Idndex out of bounds");
1135
        // get lod
1136
0
        MeshLodUsage* lod = &(mMeshLodUsageList[index]);
1137
1138
0
        lod->manualName = meshName;
1139
0
        lod->manualMesh.reset();
1140
0
        OGRE_DELETE lod->edgeData;
1141
0
        lod->edgeData = 0;
1142
0
    }
1143
    //---------------------------------------------------------------------
1144
    void Mesh::_setLodInfo(unsigned short numLevels)
1145
4.71k
    {
1146
4.71k
        assert(!mEdgeListsBuilt && "Can't modify LOD after edge lists built");
1147
1148
        // Basic prerequisites
1149
4.71k
        OgreAssert(numLevels > 0,  "Must be at least one level (full detail level must exist)");
1150
1151
4.71k
        mMeshLodUsageList.resize(numLevels);
1152
        // Resize submesh face data lists too
1153
4.71k
        for (auto & i : mSubMeshList)
1154
1.89k
        {
1155
1.89k
            i->mLodFaceList.resize(numLevels - 1);
1156
1.89k
        }
1157
4.71k
    }
1158
    //---------------------------------------------------------------------
1159
    void Mesh::_setLodUsage(unsigned short level, const MeshLodUsage& usage)
1160
0
    {
1161
0
        assert(!mEdgeListsBuilt && "Can't modify LOD after edge lists built");
1162
1163
        // Basic prerequisites
1164
0
        assert(level != 0 && "Can't modify first LOD level (full detail)");
1165
0
        assert(level < mMeshLodUsageList.size() && "Index out of bounds");
1166
1167
0
        mMeshLodUsageList[level] = usage;
1168
1169
0
        if(!mMeshLodUsageList[level].manualName.empty()){
1170
0
            mHasManualLodLevel = true;
1171
0
        }
1172
0
    }
1173
    //---------------------------------------------------------------------
1174
    void Mesh::_setSubMeshLodFaceList(unsigned short subIdx, unsigned short level,
1175
        IndexData* facedata)
1176
0
    {
1177
0
        assert(!mEdgeListsBuilt && "Can't modify LOD after edge lists built");
1178
1179
        // Basic prerequisites
1180
0
        assert(mMeshLodUsageList[level].manualName.empty() && "Not using generated LODs!");
1181
0
        assert(subIdx < mSubMeshList.size() && "Index out of bounds");
1182
0
        assert(level != 0 && "Can't modify first LOD level (full detail)");
1183
0
        assert(level-1 < (unsigned short)mSubMeshList[subIdx]->mLodFaceList.size() && "Index out of bounds");
1184
1185
0
        SubMesh* sm = mSubMeshList[subIdx];
1186
0
        sm->mLodFaceList[level - 1] = facedata;
1187
0
    }
1188
#endif
1189
    //---------------------------------------------------------------------
1190
    bool Mesh::_isManualLodLevel( unsigned short level ) const
1191
0
    {
1192
0
#if !OGRE_NO_MESHLOD
1193
0
        return !mMeshLodUsageList[level].manualName.empty();
1194
#else
1195
        return false;
1196
#endif
1197
0
    }
1198
    //---------------------------------------------------------------------
1199
    ushort Mesh::_getSubMeshIndex(const String& name) const
1200
0
    {
1201
0
        SubMeshNameMap::const_iterator i = mSubMeshNameMap.find(name) ;
1202
0
        if (i == mSubMeshNameMap.end())
1203
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "No SubMesh named " + name + " found.",
1204
0
                "Mesh::_getSubMeshIndex");
1205
1206
0
        return i->second;
1207
0
    }
1208
    //--------------------------------------------------------------------
1209
    void Mesh::removeLodLevels(void)
1210
4.96k
    {
1211
4.96k
#if !OGRE_NO_MESHLOD
1212
        // Remove data from SubMeshes
1213
4.96k
        for (auto *isub : mSubMeshList)
1214
0
        {
1215
0
            isub->removeLodLevels();
1216
0
        }
1217
1218
4.96k
        bool edgeListWasBuilt = isEdgeListBuilt();
1219
4.96k
        freeEdgeList();
1220
1221
        // Reinitialise
1222
4.96k
        mMeshLodUsageList.resize(1);
1223
4.96k
        mMeshLodUsageList[0].edgeData = NULL;
1224
1225
4.96k
        if(edgeListWasBuilt)
1226
0
            buildEdgeList();
1227
4.96k
#endif
1228
4.96k
    }
1229
1230
    //---------------------------------------------------------------------
1231
    Real Mesh::getBoundingSphereRadius(void) const
1232
0
    {
1233
0
        return mBoundRadius;
1234
0
    }
1235
    //---------------------------------------------------------------------
1236
    Real Mesh::getBoneBoundingRadius(void) const
1237
0
    {
1238
0
        return mBoneBoundingRadius;
1239
0
    }
1240
    //---------------------------------------------------------------------
1241
    void Mesh::setVertexBufferPolicy(HardwareBuffer::Usage vbUsage, bool shadowBuffer)
1242
0
    {
1243
0
        mVertexBufferUsage = (HardwareBufferUsage)vbUsage;
1244
0
        mVertexBufferShadowBuffer = shadowBuffer;
1245
0
    }
1246
    //---------------------------------------------------------------------
1247
    void Mesh::setIndexBufferPolicy(HardwareBuffer::Usage vbUsage, bool shadowBuffer)
1248
0
    {
1249
0
        mIndexBufferUsage = (HardwareBufferUsage)vbUsage;
1250
0
        mIndexBufferShadowBuffer = shadowBuffer;
1251
0
    }
1252
    //---------------------------------------------------------------------
1253
    void Mesh::mergeAdjacentTexcoords( unsigned short finalTexCoordSet,
1254
                                        unsigned short texCoordSetToDestroy )
1255
0
    {
1256
0
        if( sharedVertexData )
1257
0
            mergeAdjacentTexcoords( finalTexCoordSet, texCoordSetToDestroy, sharedVertexData );
1258
1259
0
        for (auto *s : mSubMeshList)
1260
0
        {
1261
0
            if(!s->useSharedVertices)
1262
0
                mergeAdjacentTexcoords (finalTexCoordSet, texCoordSetToDestroy, s->vertexData );
1263
0
        }
1264
0
    }
1265
    //---------------------------------------------------------------------
1266
    void Mesh::mergeAdjacentTexcoords( unsigned short finalTexCoordSet,
1267
                                        unsigned short texCoordSetToDestroy,
1268
                                        VertexData *vertexData )
1269
0
    {
1270
0
        VertexDeclaration *vDecl    = vertexData->vertexDeclaration;
1271
1272
0
        const VertexElement *uv0 = vDecl->findElementBySemantic( VES_TEXTURE_COORDINATES,
1273
0
                                                                    finalTexCoordSet );
1274
0
        const VertexElement *uv1 = vDecl->findElementBySemantic( VES_TEXTURE_COORDINATES,
1275
0
                                                                    texCoordSetToDestroy );
1276
1277
0
        if( uv0 && uv1 )
1278
0
        {
1279
            //Check that both base types are compatible (mix floats w/ shorts) and there's enough space
1280
0
            VertexElementType baseType0 = VertexElement::getBaseType( uv0->getType() );
1281
0
            VertexElementType baseType1 = VertexElement::getBaseType( uv1->getType() );
1282
1283
0
            unsigned short totalTypeCount = VertexElement::getTypeCount( uv0->getType() ) +
1284
0
                                            VertexElement::getTypeCount( uv1->getType() );
1285
0
            if( baseType0 == baseType1 && totalTypeCount <= 4 )
1286
0
            {
1287
0
                const VertexDeclaration::VertexElementList &veList = vDecl->getElements();
1288
0
                VertexDeclaration::VertexElementList::const_iterator uv0Itor = std::find( veList.begin(),
1289
0
                                                                                    veList.end(), *uv0 );
1290
0
                unsigned short elem_idx     = std::distance( veList.begin(), uv0Itor );
1291
0
                VertexElementType newType   = VertexElement::multiplyTypeCount( baseType0,
1292
0
                                                                                totalTypeCount );
1293
1294
0
                if( ( uv0->getOffset() + uv0->getSize() == uv1->getOffset() ||
1295
0
                      uv1->getOffset() + uv1->getSize() == uv0->getOffset() ) &&
1296
0
                    uv0->getSource() == uv1->getSource() )
1297
0
                {
1298
                    //Special case where they adjacent, just change the declaration & we're done.
1299
0
                    size_t newOffset        = std::min( uv0->getOffset(), uv1->getOffset() );
1300
0
                    unsigned short newIdx   = std::min( uv0->getIndex(), uv1->getIndex() );
1301
1302
0
                    vDecl->modifyElement( elem_idx, uv0->getSource(), newOffset, newType,
1303
0
                                            VES_TEXTURE_COORDINATES, newIdx );
1304
0
                    vDecl->removeElement( VES_TEXTURE_COORDINATES, texCoordSetToDestroy );
1305
0
                    uv1 = 0;
1306
0
                }
1307
1308
0
                vDecl->closeGapsInSource();
1309
0
            }
1310
0
        }
1311
0
    }
1312
    //---------------------------------------------------------------------
1313
    void Mesh::organiseTangentsBuffer(VertexData *vertexData,
1314
        VertexElementSemantic targetSemantic, unsigned short index,
1315
        unsigned short sourceTexCoordSet)
1316
0
    {
1317
0
        VertexDeclaration *vDecl = vertexData->vertexDeclaration ;
1318
0
        VertexBufferBinding *vBind = vertexData->vertexBufferBinding ;
1319
1320
0
        const VertexElement *tangentsElem = vDecl->findElementBySemantic(targetSemantic, index);
1321
0
        bool needsToBeCreated = false;
1322
1323
0
        if (!tangentsElem)
1324
0
        { // no tex coords with index 1
1325
0
                needsToBeCreated = true ;
1326
0
        }
1327
0
        else if (tangentsElem->getType() != VET_FLOAT3)
1328
0
        {
1329
            //  buffer exists, but not 3D
1330
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
1331
0
                "Target semantic set already exists but is not 3D, therefore "
1332
0
                "cannot contain tangents. Pick an alternative destination semantic. ",
1333
0
                "Mesh::organiseTangentsBuffer");
1334
0
        }
1335
1336
0
        HardwareVertexBufferSharedPtr newBuffer;
1337
0
        if (needsToBeCreated)
1338
0
        {
1339
            // To be most efficient with our vertex streams,
1340
            // tack the new tangents onto the same buffer as the
1341
            // source texture coord set
1342
0
            const VertexElement* prevTexCoordElem =
1343
0
                vertexData->vertexDeclaration->findElementBySemantic(
1344
0
                    VES_TEXTURE_COORDINATES, sourceTexCoordSet);
1345
0
            if (!prevTexCoordElem)
1346
0
            {
1347
0
                OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
1348
0
                    "Cannot locate the first texture coordinate element to "
1349
0
                    "which to append the new tangents.",
1350
0
                    "Mesh::orgagniseTangentsBuffer");
1351
0
            }
1352
            // Find the buffer associated with  this element
1353
0
            HardwareVertexBufferSharedPtr origBuffer =
1354
0
                vertexData->vertexBufferBinding->getBuffer(
1355
0
                    prevTexCoordElem->getSource());
1356
            // Now create a new buffer, which includes the previous contents
1357
            // plus extra space for the 3D coords
1358
0
            newBuffer = getHardwareBufferManager()->createVertexBuffer(
1359
0
                origBuffer->getVertexSize() + 3*sizeof(float),
1360
0
                vertexData->vertexCount,
1361
0
                origBuffer->getUsage(),
1362
0
                origBuffer->hasShadowBuffer() );
1363
            // Add the new element
1364
0
            vDecl->addElement(
1365
0
                prevTexCoordElem->getSource(),
1366
0
                origBuffer->getVertexSize(),
1367
0
                VET_FLOAT3,
1368
0
                targetSemantic,
1369
0
                index);
1370
            // Now copy the original data across
1371
0
            HardwareBufferLockGuard srcLock(origBuffer, HardwareBuffer::HBL_READ_ONLY);
1372
0
            HardwareBufferLockGuard dstLock(newBuffer, HardwareBuffer::HBL_DISCARD);
1373
0
            unsigned char* pSrc = static_cast<unsigned char*>(srcLock.pData);
1374
0
            unsigned char* pDest = static_cast<unsigned char*>(dstLock.pData);
1375
0
            size_t vertSize = origBuffer->getVertexSize();
1376
0
            for (size_t v = 0; v < vertexData->vertexCount; ++v)
1377
0
            {
1378
                // Copy original vertex data
1379
0
                memcpy(pDest, pSrc, vertSize);
1380
0
                pSrc += vertSize;
1381
0
                pDest += vertSize;
1382
                // Set the new part to 0 since we'll accumulate in this
1383
0
                memset(pDest, 0, sizeof(float)*3);
1384
0
                pDest += sizeof(float)*3;
1385
0
            }
1386
1387
            // Rebind the new buffer
1388
0
            vBind->setBinding(prevTexCoordElem->getSource(), newBuffer);
1389
0
        }
1390
0
    }
1391
    //---------------------------------------------------------------------
1392
    void Mesh::buildTangentVectors(unsigned short sourceTexCoordSet, bool splitMirrored, bool splitRotated,
1393
                                   bool storeParityInW)
1394
0
    {
1395
1396
0
        TangentSpaceCalc tangentsCalc;
1397
0
        tangentsCalc.setSplitMirrored(splitMirrored);
1398
0
        tangentsCalc.setSplitRotated(splitRotated);
1399
0
        tangentsCalc.setStoreParityInW(storeParityInW);
1400
1401
        // shared geometry first
1402
0
        if (sharedVertexData)
1403
0
        {
1404
0
            tangentsCalc.setVertexData(sharedVertexData);
1405
0
            bool found = false;
1406
0
            for (auto sm : mSubMeshList)
1407
0
            {
1408
0
                if (sm->useSharedVertices)
1409
0
                {
1410
0
                    tangentsCalc.addIndexData(sm->indexData);
1411
0
                    found = true;
1412
0
                }
1413
0
            }
1414
0
            if (found)
1415
0
            {
1416
0
                TangentSpaceCalc::Result res = tangentsCalc.build(sourceTexCoordSet);
1417
1418
                // If any vertex splitting happened, we have to give them bone assignments
1419
0
                if (mSkeleton)
1420
0
                {
1421
0
                    for (auto & remap : res.indexesRemapped)
1422
0
                    {
1423
                        // Copy all bone assignments from the split vertex
1424
0
                        VertexBoneAssignmentList::iterator vbstart = mBoneAssignments.lower_bound(remap.splitVertex.first);
1425
0
                        VertexBoneAssignmentList::iterator vbend = mBoneAssignments.upper_bound(remap.splitVertex.first);
1426
0
                        for (VertexBoneAssignmentList::iterator vba = vbstart; vba != vbend; ++vba)
1427
0
                        {
1428
0
                            VertexBoneAssignment newAsgn = vba->second;
1429
0
                            newAsgn.vertexIndex = static_cast<unsigned int>(remap.splitVertex.second);
1430
                            // multimap insert doesn't invalidate iterators
1431
0
                            addBoneAssignment(newAsgn);
1432
0
                        }
1433
1434
0
                    }
1435
0
                }
1436
1437
                // Update poses (some vertices might have been duplicated)
1438
                // we will just check which vertices have been split and copy
1439
                // the offset for the original vertex to the corresponding new vertex
1440
0
                for (auto *current_pose : mPoseList)
1441
0
                {
1442
0
                    const Pose::VertexOffsetMap& offset_map = current_pose->getVertexOffsets();
1443
0
                    for(auto& split : res.vertexSplits)
1444
0
                    {
1445
0
                        Pose::VertexOffsetMap::const_iterator found_offset = offset_map.find(split.first);
1446
1447
                        // copy the offset
1448
0
                        if( found_offset != offset_map.end() )
1449
0
                        {
1450
0
                            current_pose->addVertex (split.second, found_offset->second );
1451
0
                        }
1452
0
                    }
1453
0
                }
1454
0
            }
1455
0
        }
1456
1457
        // Dedicated geometry
1458
0
        for (auto sm : mSubMeshList)
1459
0
        {
1460
0
            if (!sm->useSharedVertices)
1461
0
            {
1462
0
                tangentsCalc.clear();
1463
0
                tangentsCalc.setVertexData(sm->vertexData);
1464
0
                tangentsCalc.addIndexData(sm->indexData, sm->operationType);
1465
0
                TangentSpaceCalc::Result res = tangentsCalc.build(sourceTexCoordSet);
1466
1467
                // If any vertex splitting happened, we have to give them bone assignments
1468
0
                if (mSkeleton)
1469
0
                {
1470
0
                    for (auto & remap : res.indexesRemapped)
1471
0
                    {
1472
                        // Copy all bone assignments from the split vertex
1473
0
                        VertexBoneAssignmentList::const_iterator vbstart =
1474
0
                            sm->getBoneAssignments().lower_bound(remap.splitVertex.first);
1475
0
                        VertexBoneAssignmentList::const_iterator vbend =
1476
0
                            sm->getBoneAssignments().upper_bound(remap.splitVertex.first);
1477
0
                        for (VertexBoneAssignmentList::const_iterator vba = vbstart; vba != vbend; ++vba)
1478
0
                        {
1479
0
                            VertexBoneAssignment newAsgn = vba->second;
1480
0
                            newAsgn.vertexIndex = static_cast<unsigned int>(remap.splitVertex.second);
1481
                            // multimap insert doesn't invalidate iterators
1482
0
                            sm->addBoneAssignment(newAsgn);
1483
0
                        }
1484
1485
0
                    }
1486
1487
0
                }
1488
0
            }
1489
0
        }
1490
1491
0
    }
1492
    //---------------------------------------------------------------------
1493
    bool Mesh::suggestTangentVectorBuildParams(unsigned short& outSourceCoordSet)
1494
0
    {
1495
        // Go through all the vertex data and locate source and dest (must agree)
1496
0
        bool sharedGeometryDone = false;
1497
0
        bool foundExisting = false;
1498
0
        bool firstOne = true;
1499
0
        for (auto *sm : mSubMeshList)
1500
0
        {
1501
0
            VertexData* vertexData;
1502
0
            if (sm->useSharedVertices)
1503
0
            {
1504
0
                if (sharedGeometryDone)
1505
0
                    continue;
1506
0
                vertexData = sharedVertexData;
1507
0
                sharedGeometryDone = true;
1508
0
            }
1509
0
            else
1510
0
            {
1511
0
                vertexData = sm->vertexData;
1512
0
            }
1513
1514
0
            const VertexElement *sourceElem = 0;
1515
0
            unsigned short targetIndex = 0;
1516
0
            for (targetIndex = 0; targetIndex < OGRE_MAX_TEXTURE_COORD_SETS; ++targetIndex)
1517
0
            {
1518
0
                auto testElem =
1519
0
                    vertexData->vertexDeclaration->findElementBySemantic(VES_TEXTURE_COORDINATES, targetIndex);
1520
0
                if (!testElem)
1521
0
                    break; // finish if we've run out, t will be the target
1522
1523
                // We're still looking for the source texture coords
1524
0
                if (testElem->getType() == VET_FLOAT2)
1525
0
                {
1526
                    // Ok, we found it
1527
0
                    sourceElem = testElem;
1528
0
                    break;
1529
0
                }
1530
0
            }
1531
1532
            // After iterating, we should have a source and a possible destination (t)
1533
0
            if (!sourceElem)
1534
0
            {
1535
0
                OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
1536
0
                            "Cannot locate an appropriate 2D texture coordinate set for "
1537
0
                            "all the vertex data in this mesh to create tangents from. ");
1538
0
            }
1539
1540
            // Look for existing semantic
1541
0
            foundExisting = vertexData->vertexDeclaration->findElementBySemantic(VES_TANGENT);
1542
1543
            // Check that we agree with previous decisions, if this is not the
1544
            // first one, and if we're not just using the existing one
1545
0
            if (!firstOne && !foundExisting)
1546
0
            {
1547
0
                if (sourceElem->getIndex() != outSourceCoordSet)
1548
0
                {
1549
0
                    OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
1550
0
                                "Multiple sets of vertex data in this mesh disagree on "
1551
0
                                "the appropriate index to use for the texture coordinates. "
1552
0
                                "This ambiguity must be rectified before tangents can be generated.");
1553
0
                }
1554
0
            }
1555
1556
            // Otherwise, save this result
1557
0
            outSourceCoordSet = sourceElem->getIndex();
1558
1559
0
            firstOne = false;
1560
1561
0
       }
1562
1563
0
        return foundExisting;
1564
1565
0
    }
1566
    //---------------------------------------------------------------------
1567
    void Mesh::buildEdgeList(void)
1568
0
    {
1569
0
        if (mEdgeListsBuilt)
1570
0
            return;
1571
0
#if !OGRE_NO_MESHLOD
1572
        // Loop over LODs
1573
0
        for (unsigned short lodIndex = 0; lodIndex < (unsigned short)mMeshLodUsageList.size(); ++lodIndex)
1574
0
        {
1575
            // use getLodLevel to enforce loading of manual mesh lods
1576
0
            const MeshLodUsage& usage = getLodLevel(lodIndex);
1577
1578
0
            if (!usage.manualName.empty() && lodIndex != 0)
1579
0
            {
1580
                // Delegate edge building to manual mesh
1581
                // It should have already built it's own edge list while loading
1582
0
                if (usage.manualMesh)
1583
0
                {
1584
0
                    usage.edgeData = usage.manualMesh->getEdgeList(0);
1585
0
                }
1586
0
            }
1587
0
            else
1588
0
            {
1589
                // Build
1590
0
                EdgeListBuilder eb;
1591
0
                size_t vertexSetCount = 0;
1592
0
                bool atLeastOneIndexSet = false;
1593
1594
0
                if (sharedVertexData)
1595
0
                {
1596
0
                    eb.addVertexData(sharedVertexData);
1597
0
                    vertexSetCount++;
1598
0
                }
1599
1600
                // Prepare the builder using the submesh information
1601
0
                for (auto *s : mSubMeshList)
1602
0
                {
1603
0
                    if (s->operationType != RenderOperation::OT_TRIANGLE_FAN &&
1604
0
                        s->operationType != RenderOperation::OT_TRIANGLE_LIST &&
1605
0
                        s->operationType != RenderOperation::OT_TRIANGLE_STRIP)
1606
0
                    {
1607
0
                        continue;
1608
0
                    }
1609
0
                    if (s->useSharedVertices)
1610
0
                    {
1611
                        // Use shared vertex data, index as set 0
1612
0
                        if (lodIndex == 0)
1613
0
                        {
1614
0
                            eb.addIndexData(s->indexData, 0, s->operationType);
1615
0
                        }
1616
0
                        else
1617
0
                        {
1618
0
                            eb.addIndexData(s->mLodFaceList[lodIndex-1], 0,
1619
0
                                s->operationType);
1620
0
                        }
1621
0
                    }
1622
0
                    else if(s->isBuildEdgesEnabled())
1623
0
                    {
1624
                        // own vertex data, add it and reference it directly
1625
0
                        eb.addVertexData(s->vertexData);
1626
0
                        if (lodIndex == 0)
1627
0
                        {
1628
                            // Base index data
1629
0
                            eb.addIndexData(s->indexData, vertexSetCount++,
1630
0
                                s->operationType);
1631
0
                        }
1632
0
                        else
1633
0
                        {
1634
                            // LOD index data
1635
0
                            eb.addIndexData(s->mLodFaceList[lodIndex-1],
1636
0
                                vertexSetCount++, s->operationType);
1637
0
                        }
1638
1639
0
                    }
1640
0
                    atLeastOneIndexSet = true;
1641
0
                }
1642
1643
0
                if (atLeastOneIndexSet)
1644
0
                {
1645
0
                    usage.edgeData = eb.build();
1646
1647
                #if OGRE_DEBUG_MODE
1648
                    // Override default log
1649
                    Log* log = LogManager::getSingleton().createLog(
1650
                        mName + "_lod" + StringConverter::toString(lodIndex) +
1651
                        "_prepshadow.log", false, false);
1652
                    usage.edgeData->log(log);
1653
                    // clean up log & close file handle
1654
                    LogManager::getSingleton().destroyLog(log);
1655
                #endif
1656
0
                }
1657
0
                else
1658
0
                {
1659
                    // create empty edge data
1660
0
                    usage.edgeData = OGRE_NEW EdgeData();
1661
0
                }
1662
0
            }
1663
0
        }
1664
#else
1665
        // Build
1666
        EdgeListBuilder eb;
1667
        size_t vertexSetCount = 0;
1668
        if (sharedVertexData)
1669
        {
1670
            eb.addVertexData(sharedVertexData);
1671
            vertexSetCount++;
1672
        }
1673
1674
        // Prepare the builder using the submesh information
1675
        for (auto *s : mSubMeshList)
1676
        {
1677
            if (s->operationType != RenderOperation::OT_TRIANGLE_FAN &&
1678
                s->operationType != RenderOperation::OT_TRIANGLE_LIST &&
1679
                s->operationType != RenderOperation::OT_TRIANGLE_STRIP)
1680
            {
1681
                continue;
1682
            }
1683
            if (s->useSharedVertices)
1684
            {
1685
                eb.addIndexData(s->indexData, 0, s->operationType);
1686
            }
1687
            else if(s->isBuildEdgesEnabled())
1688
            {
1689
                // own vertex data, add it and reference it directly
1690
                eb.addVertexData(s->vertexData);
1691
                // Base index data
1692
                eb.addIndexData(s->indexData, vertexSetCount++,
1693
                    s->operationType);
1694
            }
1695
        }
1696
1697
        mMeshLodUsageList[0].edgeData = eb.build();
1698
1699
#if OGRE_DEBUG_MODE
1700
        // Override default log
1701
        Log* log = LogManager::getSingleton().createLog(
1702
            mName + "_lod0"+
1703
            "_prepshadow.log", false, false);
1704
        mMeshLodUsageList[0].edgeData->log(log);
1705
        // clean up log & close file handle
1706
        LogManager::getSingleton().destroyLog(log);
1707
#endif
1708
#endif
1709
0
        mEdgeListsBuilt = true;
1710
0
    }
1711
    //---------------------------------------------------------------------
1712
    void Mesh::freeEdgeList(void)
1713
9.93k
    {
1714
9.93k
        if (!mEdgeListsBuilt)
1715
9.46k
            return;
1716
467
#if !OGRE_NO_MESHLOD
1717
        // Loop over LODs
1718
467
        unsigned short index = 0;
1719
467
        for (auto& usage : mMeshLodUsageList)
1720
730
        {
1721
730
            if (usage.manualName.empty() || index == 0)
1722
678
            {
1723
                // Only delete if we own this data
1724
                // Manual LODs > 0 own their own
1725
678
                OGRE_DELETE usage.edgeData;
1726
678
            }
1727
730
            usage.edgeData = NULL;
1728
730
            ++index;
1729
730
        }
1730
#else
1731
        OGRE_DELETE mMeshLodUsageList[0].edgeData;
1732
        mMeshLodUsageList[0].edgeData = NULL;
1733
#endif
1734
467
        mEdgeListsBuilt = false;
1735
467
    }
1736
    //---------------------------------------------------------------------
1737
    void Mesh::prepareForShadowVolume(void)
1738
0
    {
1739
0
        if (mPreparedForShadowVolumes)
1740
0
            return;
1741
1742
0
        if (sharedVertexData)
1743
0
        {
1744
0
            sharedVertexData->prepareForShadowVolume();
1745
0
        }
1746
0
        for (auto *s : mSubMeshList)
1747
0
        {
1748
0
            if (!s->useSharedVertices &&
1749
0
                (s->operationType == RenderOperation::OT_TRIANGLE_FAN ||
1750
0
                s->operationType == RenderOperation::OT_TRIANGLE_LIST ||
1751
0
                s->operationType == RenderOperation::OT_TRIANGLE_STRIP))
1752
0
            {
1753
0
                s->vertexData->prepareForShadowVolume();
1754
0
            }
1755
0
        }
1756
0
        mPreparedForShadowVolumes = true;
1757
0
    }
1758
    //---------------------------------------------------------------------
1759
    EdgeData* Mesh::getEdgeList(unsigned short lodIndex)
1760
0
    {
1761
        // Build edge list on demand
1762
0
        if (!mEdgeListsBuilt && mAutoBuildEdgeLists)
1763
0
        {
1764
0
            buildEdgeList();
1765
0
        }
1766
0
#if !OGRE_NO_MESHLOD
1767
0
        return getLodLevel(lodIndex).edgeData;
1768
#else
1769
        assert(lodIndex == 0);
1770
        return mMeshLodUsageList[0].edgeData;
1771
#endif
1772
0
    }
1773
    //---------------------------------------------------------------------
1774
    const EdgeData* Mesh::getEdgeList(unsigned short lodIndex) const
1775
0
    {
1776
0
#if !OGRE_NO_MESHLOD
1777
0
        return getLodLevel(lodIndex).edgeData;
1778
#else
1779
        assert(lodIndex == 0);
1780
        return mMeshLodUsageList[0].edgeData;
1781
#endif
1782
0
    }
1783
    //---------------------------------------------------------------------
1784
    void Mesh::prepareMatricesForVertexBlend(const Affine3** blendMatrices,
1785
        const Affine3* boneMatrices, const IndexMap& indexMap)
1786
0
    {
1787
0
        assert(indexMap.size() <= OGRE_MAX_NUM_BONES);
1788
0
        for (auto& i : indexMap)
1789
0
        {
1790
0
            *blendMatrices++ = boneMatrices + i;
1791
0
        }
1792
0
    }
1793
    //---------------------------------------------------------------------
1794
    void Mesh::softwareVertexBlend(const VertexData* sourceVertexData,
1795
        const VertexData* targetVertexData,
1796
        const Affine3* const* blendMatrices, size_t numMatrices,
1797
        bool blendNormals)
1798
0
    {
1799
0
        float *pSrcPos = 0;
1800
0
        float *pSrcNorm = 0;
1801
0
        float *pDestPos = 0;
1802
0
        float *pDestNorm = 0;
1803
0
        float *pBlendWeight = 0;
1804
0
        unsigned char* pBlendIdx = 0;
1805
0
        size_t srcNormStride = 0;
1806
0
        size_t destNormStride = 0;
1807
1808
        // Get elements for source
1809
0
        auto srcElemPos = sourceVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
1810
0
        auto srcElemNorm = sourceVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL);
1811
0
        auto srcElemBlendIndices = sourceVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_INDICES);
1812
0
        auto srcElemBlendWeights = sourceVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_WEIGHTS);
1813
0
        OgreAssert(srcElemPos && srcElemBlendIndices && srcElemBlendWeights,
1814
0
                   "You must supply at least positions, blend indices and blend weights");
1815
        // Get elements for target
1816
0
        auto destElemPos = targetVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
1817
0
        auto destElemNorm = targetVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL);
1818
1819
        // Get buffers for source
1820
0
        HardwareVertexBufferSharedPtr srcPosBuf = sourceVertexData->vertexBufferBinding->getBuffer(srcElemPos->getSource());
1821
0
        HardwareVertexBufferSharedPtr srcIdxBuf = sourceVertexData->vertexBufferBinding->getBuffer(srcElemBlendIndices->getSource());
1822
0
        HardwareVertexBufferSharedPtr srcWeightBuf = sourceVertexData->vertexBufferBinding->getBuffer(srcElemBlendWeights->getSource());
1823
0
        HardwareVertexBufferSharedPtr srcNormBuf;
1824
1825
        // Get buffers for target
1826
0
        HardwareVertexBufferSharedPtr destPosBuf = targetVertexData->vertexBufferBinding->getBuffer(destElemPos->getSource());
1827
1828
        // Lock source buffers for reading
1829
0
        HardwareBufferLockGuard srcPosLock(srcPosBuf, HardwareBuffer::HBL_READ_ONLY);
1830
0
        srcElemPos->baseVertexPointerToElement(srcPosLock.pData, &pSrcPos);
1831
1832
        // Do we have normals and want to blend them?
1833
0
        bool includeNormals = blendNormals && srcElemNorm && destElemNorm;
1834
0
        HardwareVertexBufferSharedPtr destNormBuf;
1835
0
        HardwareBufferLockGuard srcNormLock;
1836
0
        if (includeNormals)
1837
0
        {
1838
            // Get buffers for source
1839
0
            srcNormBuf = sourceVertexData->vertexBufferBinding->getBuffer(srcElemNorm->getSource());
1840
0
            srcNormStride = srcNormBuf->getVertexSize();
1841
            // Get buffers for target
1842
0
            destNormBuf = targetVertexData->vertexBufferBinding->getBuffer(destElemNorm->getSource());
1843
0
            destNormStride = destNormBuf->getVertexSize();
1844
1845
0
            if (srcNormBuf != srcPosBuf)
1846
0
            {
1847
                // Different buffer
1848
0
                srcNormLock.lock(srcNormBuf, HardwareBuffer::HBL_READ_ONLY);
1849
0
            }
1850
0
            srcElemNorm->baseVertexPointerToElement(srcNormLock.pData ? srcNormLock.pData : srcPosLock.pData, &pSrcNorm);
1851
0
        }
1852
1853
        // Indices must be 4 bytes
1854
0
        assert(srcElemBlendIndices->getType() == VET_UBYTE4 && "Blend indices must be VET_UBYTE4");
1855
0
        HardwareBufferLockGuard srcIdxLock(srcIdxBuf, HardwareBuffer::HBL_READ_ONLY);
1856
0
        srcElemBlendIndices->baseVertexPointerToElement(srcIdxLock.pData, &pBlendIdx);
1857
0
        HardwareBufferLockGuard srcWeightLock;
1858
0
        if (srcWeightBuf != srcIdxBuf)
1859
0
        {
1860
            // Lock buffer
1861
0
            srcWeightLock.lock(srcWeightBuf, HardwareBuffer::HBL_READ_ONLY);
1862
0
        }
1863
0
        srcElemBlendWeights->baseVertexPointerToElement(srcWeightLock.pData ? srcWeightLock.pData : srcIdxLock.pData, &pBlendWeight);
1864
0
        unsigned short numWeightsPerVertex = VertexElement::getTypeCount(srcElemBlendWeights->getType());
1865
1866
        // Lock destination buffers for writing
1867
0
        HardwareBufferLockGuard destPosLock(destPosBuf,
1868
0
            (destNormBuf != destPosBuf && destPosBuf->getVertexSize() == destElemPos->getSize()) ||
1869
0
            (destNormBuf == destPosBuf && destPosBuf->getVertexSize() == destElemPos->getSize() + destElemNorm->getSize()) ?
1870
0
            HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_NORMAL);
1871
0
        destElemPos->baseVertexPointerToElement(destPosLock.pData, &pDestPos);
1872
0
        HardwareBufferLockGuard destNormLock;
1873
0
        if (includeNormals)
1874
0
        {
1875
0
            if (destNormBuf != destPosBuf)
1876
0
            {
1877
0
                destNormLock.lock(destNormBuf, destNormBuf->getVertexSize() == destElemNorm->getSize()
1878
0
                                                   ? HardwareBuffer::HBL_DISCARD
1879
0
                                                   : HardwareBuffer::HBL_NORMAL);
1880
0
            }
1881
0
            destElemNorm->baseVertexPointerToElement(destNormLock.pData ? destNormLock.pData : destPosLock.pData, &pDestNorm);
1882
0
        }
1883
1884
0
        auto srcPosStride = srcPosBuf->getVertexSize();
1885
0
        auto destPosStride = destPosBuf->getVertexSize();
1886
0
        auto blendIdxStride = srcIdxBuf->getVertexSize();
1887
0
        auto blendWeightStride = srcWeightBuf->getVertexSize();
1888
1889
0
        OptimisedUtil::getImplementation()->softwareVertexSkinning(
1890
0
            pSrcPos, pDestPos,
1891
0
            pSrcNorm, pDestNorm,
1892
0
            pBlendWeight, pBlendIdx,
1893
0
            blendMatrices,
1894
0
            srcPosStride, destPosStride,
1895
0
            srcNormStride, destNormStride,
1896
0
            blendWeightStride, blendIdxStride,
1897
0
            numWeightsPerVertex,
1898
0
            targetVertexData->vertexCount);
1899
0
    }
1900
    //---------------------------------------------------------------------
1901
    void Mesh::softwareVertexMorph(float t,
1902
        const HardwareVertexBufferSharedPtr& b1,
1903
        const HardwareVertexBufferSharedPtr& b2,
1904
        VertexData* targetVertexData)
1905
0
    {
1906
0
        HardwareBufferLockGuard b1Lock(b1, HardwareBuffer::HBL_READ_ONLY);
1907
0
        float* pb1 = static_cast<float*>(b1Lock.pData);
1908
0
        HardwareBufferLockGuard b2Lock;
1909
0
        float* pb2;
1910
0
        if (b1.get() != b2.get())
1911
0
        {
1912
0
            b2Lock.lock(b2, HardwareBuffer::HBL_READ_ONLY);
1913
0
            pb2 = static_cast<float*>(b2Lock.pData);
1914
0
        }
1915
0
        else
1916
0
        {
1917
            // Same buffer - track with only one entry or time index exactly matching
1918
            // one keyframe
1919
            // For simplicity of main code, interpolate still but with same val
1920
0
            pb2 = pb1;
1921
0
        }
1922
1923
0
        const VertexElement* posElem =
1924
0
            targetVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
1925
0
        assert(posElem);
1926
0
        const VertexElement* normElem =
1927
0
            targetVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL);
1928
1929
0
        bool morphNormals = false;
1930
0
        if (normElem && normElem->getSource() == posElem->getSource() &&
1931
0
            b1->getVertexSize() == 24 && b2->getVertexSize() == 24)
1932
0
            morphNormals = true;
1933
1934
0
        HardwareVertexBufferSharedPtr destBuf =
1935
0
            targetVertexData->vertexBufferBinding->getBuffer(
1936
0
                posElem->getSource());
1937
0
        assert((posElem->getSize() == destBuf->getVertexSize()
1938
0
                || (morphNormals && posElem->getSize() + normElem->getSize() == destBuf->getVertexSize())) &&
1939
0
            "Positions (or positions & normals) must be in a buffer on their own for morphing");
1940
0
        HardwareBufferLockGuard destLock(destBuf, HardwareBuffer::HBL_DISCARD);
1941
0
        float* pdst = static_cast<float*>(destLock.pData);
1942
1943
0
        OptimisedUtil::getImplementation()->softwareVertexMorph(
1944
0
            t, pb1, pb2, pdst,
1945
0
            b1->getVertexSize(), b2->getVertexSize(), destBuf->getVertexSize(),
1946
0
            targetVertexData->vertexCount,
1947
0
            morphNormals);
1948
0
    }
1949
    //---------------------------------------------------------------------
1950
    void Mesh::softwareVertexPoseBlend(float weight,
1951
        const std::map<uint32, Vector3f>& vertexOffsetMap,
1952
        const std::map<uint32, Vector3f>& normalsMap,
1953
        VertexData* targetVertexData)
1954
0
    {
1955
        // Do nothing if no weight
1956
0
        if (weight == 0.0f)
1957
0
            return;
1958
1959
0
        const VertexElement* posElem =
1960
0
            targetVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
1961
0
        const VertexElement* normElem =
1962
0
            targetVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL);
1963
0
        assert(posElem);
1964
        // Support normals if they're in the same buffer as positions and pose includes them
1965
0
        bool normals = normElem && !normalsMap.empty() && posElem->getSource() == normElem->getSource();
1966
0
        HardwareVertexBufferSharedPtr destBuf =
1967
0
            targetVertexData->vertexBufferBinding->getBuffer(
1968
0
            posElem->getSource());
1969
1970
0
        size_t elemsPerVertex = destBuf->getVertexSize()/sizeof(float);
1971
1972
        // Have to lock in normal mode since this is incremental
1973
0
        HardwareBufferLockGuard destLock(destBuf, HardwareBuffer::HBL_NORMAL);
1974
0
        float* pBase = static_cast<float*>(destLock.pData);
1975
1976
        // Iterate over affected vertices
1977
0
        for (const auto & i : vertexOffsetMap)
1978
0
        {
1979
            // Adjust pointer
1980
0
            float *pdst = pBase + i.first*elemsPerVertex;
1981
1982
0
            *pdst = *pdst + (i.second[0] * weight);
1983
0
            ++pdst;
1984
0
            *pdst = *pdst + (i.second[01] * weight);
1985
0
            ++pdst;
1986
0
            *pdst = *pdst + (i.second[2] * weight);
1987
0
            ++pdst;
1988
1989
0
        }
1990
1991
0
        if (normals)
1992
0
        {
1993
0
            float* pNormBase;
1994
0
            normElem->baseVertexPointerToElement((void*)pBase, &pNormBase);
1995
0
            for (const auto & i : normalsMap)
1996
0
            {
1997
                // Adjust pointer
1998
0
                float *pdst = pNormBase + i.first*elemsPerVertex;
1999
2000
0
                *pdst = *pdst + (i.second[0] * weight);
2001
0
                ++pdst;
2002
0
                *pdst = *pdst + (i.second[1] * weight);
2003
0
                ++pdst;
2004
0
                *pdst = *pdst + (i.second[2] * weight);
2005
0
                ++pdst;
2006
2007
0
            }
2008
0
        }
2009
0
    }
2010
    //---------------------------------------------------------------------
2011
    size_t Mesh::calculateSize(void) const
2012
4.96k
    {
2013
        // calculate GPU size
2014
4.96k
        size_t ret = 0;
2015
4.96k
        unsigned short i;
2016
        // Shared vertices
2017
4.96k
        if (sharedVertexData)
2018
0
        {
2019
0
            for (i = 0;
2020
0
                i < sharedVertexData->vertexBufferBinding->getBufferCount();
2021
0
                ++i)
2022
0
            {
2023
0
                ret += sharedVertexData->vertexBufferBinding
2024
0
                    ->getBuffer(i)->getSizeInBytes();
2025
0
            }
2026
0
        }
2027
2028
4.96k
        for (auto *s : mSubMeshList)
2029
0
        {
2030
            // Dedicated vertices
2031
0
            if (!s->useSharedVertices)
2032
0
            {
2033
0
                for (i = 0;
2034
0
                    i < s->vertexData->vertexBufferBinding->getBufferCount();
2035
0
                    ++i)
2036
0
                {
2037
0
                    ret += s->vertexData->vertexBufferBinding
2038
0
                        ->getBuffer(i)->getSizeInBytes();
2039
0
                }
2040
0
            }
2041
0
            if (s->indexData->indexBuffer)
2042
0
            {
2043
                // Index data
2044
0
                ret += s->indexData->indexBuffer->getSizeInBytes();
2045
0
            }
2046
2047
0
        }
2048
4.96k
        return ret;
2049
4.96k
    }
2050
    //-----------------------------------------------------------------------------
2051
    bool Mesh::hasVertexAnimation(void) const
2052
0
    {
2053
0
        return !mAnimationsList.empty();
2054
0
    }
2055
    //---------------------------------------------------------------------
2056
    VertexAnimationType Mesh::getSharedVertexDataAnimationType(void) const
2057
0
    {
2058
0
        if (mAnimationTypesDirty)
2059
0
        {
2060
0
            _determineAnimationTypes();
2061
0
        }
2062
2063
0
        return mSharedVertexDataAnimationType;
2064
0
    }
2065
    //---------------------------------------------------------------------
2066
    void Mesh::_determineAnimationTypes(void) const
2067
0
    {
2068
        // Don't check flag here; since detail checks on track changes are not
2069
        // done, allow caller to force if they need to
2070
2071
        // Initialise all types to nothing
2072
0
        mSharedVertexDataAnimationType = VAT_NONE;
2073
0
        mSharedVertexDataAnimationIncludesNormals = false;
2074
0
        for (auto i : mSubMeshList)
2075
0
        {
2076
0
            i->mVertexAnimationType = VAT_NONE;
2077
0
            i->mVertexAnimationIncludesNormals = false;
2078
0
        }
2079
2080
0
        mPosesIncludeNormals = false;
2081
0
        for (PoseList::const_iterator i = mPoseList.begin(); i != mPoseList.end(); ++i)
2082
0
        {
2083
0
            if (i == mPoseList.begin())
2084
0
                mPosesIncludeNormals = (*i)->getIncludesNormals();
2085
0
            else if (mPosesIncludeNormals != (*i)->getIncludesNormals())
2086
                // only support normals if consistently included
2087
0
                mPosesIncludeNormals = mPosesIncludeNormals && (*i)->getIncludesNormals();
2088
0
        }
2089
2090
        // Scan all animations and determine the type of animation tracks
2091
        // relating to each vertex data
2092
0
        for(const auto& ai : mAnimationsList)
2093
0
        {
2094
0
            for (const auto& vit : ai.second->_getVertexTrackList())
2095
0
            {
2096
0
                VertexAnimationTrack* track = vit.second;
2097
0
                ushort handle = vit.first;
2098
0
                if (handle == 0)
2099
0
                {
2100
                    // shared data
2101
0
                    if (mSharedVertexDataAnimationType != VAT_NONE &&
2102
0
                        mSharedVertexDataAnimationType != track->getAnimationType())
2103
0
                    {
2104
                        // Mixing of morph and pose animation on same data is not allowed
2105
0
                        OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
2106
0
                            "Animation tracks for shared vertex data on mesh "
2107
0
                            + mName + " try to mix vertex animation types, which is "
2108
0
                            "not allowed.",
2109
0
                            "Mesh::_determineAnimationTypes");
2110
0
                    }
2111
0
                    mSharedVertexDataAnimationType = track->getAnimationType();
2112
0
                    if (track->getAnimationType() == VAT_MORPH)
2113
0
                        mSharedVertexDataAnimationIncludesNormals = track->getVertexAnimationIncludesNormals();
2114
0
                    else
2115
0
                        mSharedVertexDataAnimationIncludesNormals = mPosesIncludeNormals;
2116
2117
0
                }
2118
0
                else
2119
0
                {
2120
                    // submesh index (-1)
2121
0
                    SubMesh* sm = getSubMesh(handle-1);
2122
0
                    if (sm->mVertexAnimationType != VAT_NONE &&
2123
0
                        sm->mVertexAnimationType != track->getAnimationType())
2124
0
                    {
2125
                        // Mixing of morph and pose animation on same data is not allowed
2126
0
                        OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
2127
0
                            "Animation tracks for dedicated vertex data "
2128
0
                            + StringConverter::toString(handle-1) + " on mesh "
2129
0
                            + mName + " try to mix vertex animation types, which is "
2130
0
                            "not allowed.",
2131
0
                            "Mesh::_determineAnimationTypes");
2132
0
                    }
2133
0
                    sm->mVertexAnimationType = track->getAnimationType();
2134
0
                    if (track->getAnimationType() == VAT_MORPH)
2135
0
                        sm->mVertexAnimationIncludesNormals = track->getVertexAnimationIncludesNormals();
2136
0
                    else
2137
0
                        sm->mVertexAnimationIncludesNormals = mPosesIncludeNormals;
2138
2139
0
                }
2140
0
            }
2141
0
        }
2142
2143
0
        mAnimationTypesDirty = false;
2144
0
    }
2145
    //---------------------------------------------------------------------
2146
    Animation* Mesh::createAnimation(const String& name, Real length)
2147
866
    {
2148
        // Check name not used
2149
866
        if (mAnimationsList.find(name) != mAnimationsList.end())
2150
2
        {
2151
2
            OGRE_EXCEPT(
2152
2
                Exception::ERR_DUPLICATE_ITEM,
2153
2
                "An animation with the name " + name + " already exists",
2154
2
                "Mesh::createAnimation");
2155
2
        }
2156
2157
864
        Animation* ret = OGRE_NEW Animation(name, length);
2158
864
        ret->_notifyContainer(this);
2159
2160
        // Add to list
2161
864
        mAnimationsList[name] = ret;
2162
2163
        // Mark animation types dirty
2164
864
        mAnimationTypesDirty = true;
2165
2166
864
        return ret;
2167
2168
866
    }
2169
    //---------------------------------------------------------------------
2170
    Animation* Mesh::getAnimation(const String& name) const
2171
0
    {
2172
0
        Animation* ret = _getAnimationImpl(name);
2173
0
        if (!ret)
2174
0
        {
2175
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
2176
0
                "No animation entry found named " + name,
2177
0
                "Mesh::getAnimation");
2178
0
        }
2179
2180
0
        return ret;
2181
0
    }
2182
    //---------------------------------------------------------------------
2183
    Animation* Mesh::getAnimation(unsigned short index) const
2184
0
    {
2185
        // If you hit this assert, then the index is out of bounds.
2186
0
        assert( index < mAnimationsList.size() );
2187
2188
0
        AnimationList::const_iterator i = mAnimationsList.begin();
2189
2190
0
        std::advance(i, index);
2191
2192
0
        return i->second;
2193
2194
0
    }
2195
    //---------------------------------------------------------------------
2196
    unsigned short Mesh::getNumAnimations(void) const
2197
0
    {
2198
0
        return static_cast<unsigned short>(mAnimationsList.size());
2199
0
    }
2200
    //---------------------------------------------------------------------
2201
    bool Mesh::hasAnimation(const String& name) const
2202
0
    {
2203
0
        return _getAnimationImpl(name) != 0;
2204
0
    }
2205
    //---------------------------------------------------------------------
2206
    Animation* Mesh::_getAnimationImpl(const String& name) const
2207
0
    {
2208
0
        Animation* ret = 0;
2209
0
        AnimationList::const_iterator i = mAnimationsList.find(name);
2210
2211
0
        if (i != mAnimationsList.end())
2212
0
        {
2213
0
            ret = i->second;
2214
0
        }
2215
2216
0
        return ret;
2217
2218
0
    }
2219
    //---------------------------------------------------------------------
2220
    void Mesh::removeAnimation(const String& name)
2221
0
    {
2222
0
        AnimationList::iterator i = mAnimationsList.find(name);
2223
2224
0
        if (i == mAnimationsList.end())
2225
0
        {
2226
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "No animation entry found named " + name,
2227
0
                "Mesh::getAnimation");
2228
0
        }
2229
2230
0
        OGRE_DELETE i->second;
2231
2232
0
        mAnimationsList.erase(i);
2233
2234
0
        mAnimationTypesDirty = true;
2235
0
    }
2236
    //---------------------------------------------------------------------
2237
    void Mesh::removeAllAnimations(void)
2238
4.96k
    {
2239
4.96k
        for (auto& a : mAnimationsList)
2240
864
        {
2241
864
            OGRE_DELETE a.second;
2242
864
        }
2243
4.96k
        mAnimationsList.clear();
2244
4.96k
        mAnimationTypesDirty = true;
2245
4.96k
    }
2246
    //---------------------------------------------------------------------
2247
    VertexData* Mesh::getVertexDataByTrackHandle(unsigned short handle)
2248
332
    {
2249
332
        if (handle == 0)
2250
308
        {
2251
308
            return sharedVertexData;
2252
308
        }
2253
24
        else
2254
24
        {
2255
24
            return getSubMesh(handle-1)->vertexData;
2256
24
        }
2257
332
    }
2258
    //---------------------------------------------------------------------
2259
    Pose* Mesh::createPose(ushort target, const String& name)
2260
36.5k
    {
2261
36.5k
        Pose* retPose = OGRE_NEW Pose(target, name);
2262
36.5k
        mPoseList.push_back(retPose);
2263
36.5k
        return retPose;
2264
36.5k
    }
2265
    //---------------------------------------------------------------------
2266
    Pose* Mesh::getPose(const String& name) const
2267
0
    {
2268
0
        for (auto i : mPoseList)
2269
0
        {
2270
0
            if (i->getName() == name)
2271
0
                return i;
2272
0
        }
2273
0
        StringStream str;
2274
0
        str << "No pose called " << name << " found in Mesh " << mName;
2275
0
        OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
2276
0
            str.str(),
2277
0
            "Mesh::getPose");
2278
2279
0
    }
2280
    //---------------------------------------------------------------------
2281
    void Mesh::removePose(ushort index)
2282
0
    {
2283
0
        OgreAssert(index < mPoseList.size(), "");
2284
0
        PoseList::iterator i = mPoseList.begin();
2285
0
        std::advance(i, index);
2286
0
        OGRE_DELETE *i;
2287
0
        mPoseList.erase(i);
2288
2289
0
    }
2290
    //---------------------------------------------------------------------
2291
    void Mesh::removePose(const String& name)
2292
0
    {
2293
0
        for (PoseList::iterator i = mPoseList.begin(); i != mPoseList.end(); ++i)
2294
0
        {
2295
0
            if ((*i)->getName() == name)
2296
0
            {
2297
0
                OGRE_DELETE *i;
2298
0
                mPoseList.erase(i);
2299
0
                return;
2300
0
            }
2301
0
        }
2302
0
        StringStream str;
2303
0
        str << "No pose called " << name << " found in Mesh " << mName;
2304
0
        OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
2305
0
            str.str(),
2306
0
            "Mesh::removePose");
2307
0
    }
2308
    //---------------------------------------------------------------------
2309
    void Mesh::removeAllPoses(void)
2310
4.96k
    {
2311
4.96k
        for (auto & i : mPoseList)
2312
36.5k
        {
2313
36.5k
            OGRE_DELETE i;
2314
36.5k
        }
2315
4.96k
        mPoseList.clear();
2316
4.96k
    }
2317
    //---------------------------------------------------------------------
2318
    Mesh::PoseIterator Mesh::getPoseIterator(void)
2319
0
    {
2320
0
        return PoseIterator(mPoseList.begin(), mPoseList.end());
2321
0
    }
2322
    //---------------------------------------------------------------------
2323
    Mesh::ConstPoseIterator Mesh::getPoseIterator(void) const
2324
0
    {
2325
0
        return ConstPoseIterator(mPoseList.begin(), mPoseList.end());
2326
0
    }
2327
    //-----------------------------------------------------------------------------
2328
    const PoseList& Mesh::getPoseList(void) const
2329
0
    {
2330
0
        return mPoseList;
2331
0
    }
2332
    //---------------------------------------------------------------------
2333
    const LodStrategy *Mesh::getLodStrategy() const
2334
4.96k
    {
2335
4.96k
        return mLodStrategy;
2336
4.96k
    }
2337
#if !OGRE_NO_MESHLOD
2338
    //---------------------------------------------------------------------
2339
    void Mesh::setLodStrategy(LodStrategy *lodStrategy)
2340
5.05k
    {
2341
5.05k
        mLodStrategy = lodStrategy;
2342
2343
5.05k
        assert(mMeshLodUsageList.size());
2344
2345
        // Re-transform user LOD values (starting at index 1, no need to transform base value)
2346
5.05k
        for (auto& m : mMeshLodUsageList)
2347
5.07k
            m.value = mLodStrategy->transformUserValue(m.userValue);
2348
2349
        // Rewrite first value
2350
5.05k
        mMeshLodUsageList[0].value = mLodStrategy->getBaseValue();
2351
5.05k
    }
2352
#endif
2353
2354
    void Mesh::_convertVertexElement(VertexElementSemantic semantic, VertexElementType dstType)
2355
3.50k
    {
2356
3.50k
        if (sharedVertexData)
2357
617
            sharedVertexData->convertVertexElement(semantic, dstType);
2358
2359
3.50k
        for (auto s : getSubMeshes())
2360
23.3k
            if (s->vertexData)
2361
18.7k
                s->vertexData->convertVertexElement(semantic, dstType);
2362
3.50k
    }
2363
}
2364