Coverage Report

Created: 2025-11-04 07:18

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