Coverage Report

Created: 2026-05-24 07:06

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