Coverage Report

Created: 2025-10-12 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreSubMesh.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
namespace Ogre {
31
    //-----------------------------------------------------------------------
32
    SubMesh::SubMesh()
33
0
        : vertexData(0)
34
0
        , parent(0)
35
0
        , useSharedVertices(true)
36
0
        , operationType(RenderOperation::OT_TRIANGLE_LIST)
37
0
        , mBoneAssignmentsOutOfDate(false)
38
0
        , mVertexAnimationType(VAT_NONE)
39
0
        , mVertexAnimationIncludesNormals(false)
40
0
        , mBuildEdgesEnabled(true)
41
0
    {
42
0
        indexData = OGRE_NEW IndexData();
43
0
    }
44
    //-----------------------------------------------------------------------
45
    SubMesh::~SubMesh()
46
0
    {
47
0
        removeLodLevels();
48
0
        OGRE_DELETE vertexData;
49
0
        OGRE_DELETE indexData;
50
0
    }
51
52
    //-----------------------------------------------------------------------
53
    void SubMesh::setMaterialName( const String& name, const String& groupName /* = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME */)
54
0
    {
55
0
        mMaterial = MaterialManager::getSingleton().getByName(name, groupName);
56
0
    }
57
    //-----------------------------------------------------------------------
58
    const String& SubMesh::getMaterialName() const
59
0
    {
60
0
        return mMaterial ? mMaterial->getName() : BLANKSTRING;
61
0
    }
62
    //-----------------------------------------------------------------------
63
    void SubMesh::_getRenderOperation(RenderOperation& ro, ushort lodIndex)
64
0
    {
65
0
        if (lodIndex > 0 && static_cast< size_t >( lodIndex - 1 ) < mLodFaceList.size())
66
0
        {
67
            // lodIndex - 1 because we don't store full detail version in mLodFaceList
68
0
            ro.indexData = mLodFaceList[lodIndex-1];
69
0
        }
70
0
        else
71
0
        {
72
0
            ro.indexData = indexData;
73
0
        }
74
0
        ro.useIndexes = ro.indexData->indexCount != 0;
75
0
        ro.operationType = operationType;
76
0
        ro.vertexData = useSharedVertices? parent->sharedVertexData : vertexData;
77
78
0
    }
79
    //-----------------------------------------------------------------------
80
    void SubMesh::addBoneAssignment(const VertexBoneAssignment& vertBoneAssign)
81
0
    {
82
0
        OgreAssert(!useSharedVertices,
83
0
                   "This SubMesh uses shared geometry, you must assign bones to the Mesh, not the SubMesh");
84
0
        mBoneAssignments.emplace(vertBoneAssign.vertexIndex, vertBoneAssign);
85
0
        mBoneAssignmentsOutOfDate = true;
86
0
    }
87
    //-----------------------------------------------------------------------
88
    void SubMesh::clearBoneAssignments(void)
89
0
    {
90
0
        mBoneAssignments.clear();
91
0
        mBoneAssignmentsOutOfDate = true;
92
0
    }
93
94
    //-----------------------------------------------------------------------
95
    void SubMesh::_compileBoneAssignments(void)
96
0
    {
97
0
        unsigned short maxBones =
98
0
            parent->_rationaliseBoneAssignments(vertexData->vertexCount, mBoneAssignments);
99
100
0
        if (maxBones != 0)
101
0
        {
102
0
            parent->compileBoneAssignments(mBoneAssignments, maxBones, 
103
0
                blendIndexToBoneIndexMap, vertexData);
104
0
        }
105
106
0
        mBoneAssignmentsOutOfDate = false;
107
0
    }
108
    //---------------------------------------------------------------------
109
    SubMesh::BoneAssignmentIterator SubMesh::getBoneAssignmentIterator(void)
110
0
    {
111
0
        return BoneAssignmentIterator(mBoneAssignments.begin(),
112
0
            mBoneAssignments.end());
113
0
    }
114
    //---------------------------------------------------------------------
115
    void SubMesh::removeLodLevels(void)
116
0
    {
117
0
        LODFaceList::iterator lodi, lodend;
118
0
        lodend = mLodFaceList.end();
119
0
        for (lodi = mLodFaceList.begin(); lodi != lodend; ++lodi)
120
0
        {
121
0
            OGRE_DELETE *lodi;
122
0
        }
123
124
0
        mLodFaceList.clear();
125
126
0
    }
127
    //---------------------------------------------------------------------
128
    VertexAnimationType SubMesh::getVertexAnimationType(void) const
129
0
    {
130
0
        if(parent->_getAnimationTypesDirty())
131
0
        {
132
0
            parent->_determineAnimationTypes();
133
0
        }
134
0
        return mVertexAnimationType;
135
0
    }
136
    //---------------------------------------------------------------------
137
    /* To find as many points from different domains as we need,
138
     * such that those domains are from different parts of the mesh,
139
     * we implement a simplified Heckbert quantization algorithm.
140
     *
141
     * This struct is like AxisAlignedBox with some specialized methods
142
     * for doing quantization.
143
     */
144
    struct Cluster
145
    {
146
        Vector3 mMin, mMax;
147
        std::set<uint32> mIndices;
148
149
        Cluster ()
150
0
        { }
151
152
        bool empty () const
153
0
        {
154
0
            if (mIndices.empty ())
155
0
                return true;
156
0
            if (mMin == mMax)
157
0
                return true;
158
0
            return false;
159
0
        }
160
161
        float volume () const
162
0
        {
163
0
            return (mMax.x - mMin.x) * (mMax.y - mMin.y) * (mMax.z - mMin.z);
164
0
        }
165
166
        void extend (float *v)
167
0
        {
168
0
            if (v [0] < mMin.x) mMin.x = v [0];
169
0
            if (v [1] < mMin.y) mMin.y = v [1];
170
0
            if (v [2] < mMin.z) mMin.z = v [2];
171
0
            if (v [0] > mMax.x) mMax.x = v [0];
172
0
            if (v [1] > mMax.y) mMax.y = v [1];
173
0
            if (v [2] > mMax.z) mMax.z = v [2];
174
0
        }
175
176
        void computeBBox (const VertexElement *poselem, uint8 *vdata, size_t vsz)
177
0
        {
178
0
            mMin.x = mMin.y = mMin.z = Math::POS_INFINITY;
179
0
            mMax.x = mMax.y = mMax.z = Math::NEG_INFINITY;
180
181
0
            for (unsigned int idx : mIndices)
182
0
            {
183
0
                float *v;
184
0
                poselem->baseVertexPointerToElement (vdata + idx * vsz, &v);
185
0
                extend (v);
186
0
            }
187
0
        }
188
189
        Cluster split (int split_axis, const VertexElement *poselem,
190
                       uint8 *vdata, size_t vsz)
191
0
        {
192
0
            Real r = (mMin [split_axis] + mMax [split_axis]) * 0.5f;
193
0
            Cluster newbox;
194
195
            // Separate all points that are inside the new bbox
196
0
            for (std::set<uint32>::iterator i = mIndices.begin ();
197
0
                 i != mIndices.end (); )
198
0
            {
199
0
                float *v;
200
0
                poselem->baseVertexPointerToElement (vdata + *i * vsz, &v);
201
0
                if (v [split_axis] > r)
202
0
                {
203
0
                    newbox.mIndices.insert (*i);
204
0
                    std::set<uint32>::iterator x = i++;
205
0
                    mIndices.erase(x);
206
0
                }
207
0
                else
208
0
                    ++i;
209
0
            }
210
211
0
            computeBBox (poselem, vdata, vsz);
212
0
            newbox.computeBBox (poselem, vdata, vsz);
213
214
0
            return newbox;
215
0
        }
216
    };
217
    //---------------------------------------------------------------------
218
    void SubMesh::generateExtremes(size_t count)
219
0
    {
220
0
        extremityPoints.clear();
221
222
0
    if (count == 0)
223
0
      return;
224
225
        /* Currently this uses just one criteria: the points must be
226
         * as far as possible from each other. This at least ensures
227
         * that the extreme points characterise the submesh as
228
         * detailed as it's possible.
229
         */
230
231
0
        VertexData *vert = useSharedVertices ?
232
0
            parent->sharedVertexData : vertexData;
233
0
        const VertexElement *poselem = vert->vertexDeclaration->
234
0
            findElementBySemantic (VES_POSITION);
235
0
        HardwareVertexBufferSharedPtr vbuf = vert->vertexBufferBinding->
236
0
            getBuffer (poselem->getSource ());
237
0
        uint8 *vdata = (uint8 *)vbuf->lock (HardwareBuffer::HBL_READ_ONLY);
238
0
        size_t vsz = vbuf->getVertexSize ();
239
240
0
        std::vector<Cluster> boxes;
241
0
        boxes.reserve (count);
242
243
        // First of all, find min and max bounding box of the submesh
244
0
        boxes.push_back (Cluster ());
245
246
0
        if (indexData->indexCount > 0)
247
0
        {
248
249
0
            uint elsz = indexData->indexBuffer->getType () == HardwareIndexBuffer::IT_32BIT ?
250
0
                4 : 2;
251
0
            uint8 *idata = (uint8 *)indexData->indexBuffer->lock (
252
0
                indexData->indexStart * elsz, indexData->indexCount * elsz,
253
0
                HardwareIndexBuffer::HBL_READ_ONLY);
254
255
0
            for (size_t i = 0; i < indexData->indexCount; i++)
256
0
            {
257
0
                int idx = (elsz == 2) ? ((uint16 *)idata) [i] : ((uint32 *)idata) [i];
258
0
                boxes [0].mIndices.insert (idx);
259
0
            }
260
0
            indexData->indexBuffer->unlock ();
261
262
0
        }
263
0
        else
264
0
        {
265
            // just insert all indexes
266
0
            for (size_t i = vertexData->vertexStart; i < vertexData->vertexCount; i++)
267
0
            {
268
0
                boxes [0].mIndices.insert (static_cast<int>(i));
269
0
            }
270
271
0
        }
272
273
0
        boxes [0].computeBBox (poselem, vdata, vsz);
274
275
        // Remember the geometrical center of the submesh
276
0
        Vector3 center = (boxes [0].mMax + boxes [0].mMin) * 0.5;
277
278
        // Ok, now loop until we have as many boxes, as we need extremes
279
0
        while (boxes.size () < count)
280
0
        {
281
            // Find the largest box with more than one vertex :)
282
0
            Cluster *split_box = NULL;
283
0
            Real split_volume = -1;
284
0
            for (auto & boxe : boxes)
285
0
            {
286
0
                if (boxe.empty ())
287
0
                    continue;
288
0
                Real v = boxe.volume ();
289
0
                if (v > split_volume)
290
0
                {
291
0
                    split_volume = v;
292
0
                    split_box = &boxe;
293
0
                }
294
0
            }
295
296
            // If we don't have what to split, break
297
0
            if (!split_box)
298
0
                break;
299
300
            // Find the coordinate axis to split the box into two
301
0
            int split_axis = 0;
302
0
            Real split_length = split_box->mMax.x - split_box->mMin.x;
303
0
            for (int i = 1; i < 3; i++)
304
0
            {
305
0
                Real l = split_box->mMax [i] - split_box->mMin [i];
306
0
                if (l > split_length)
307
0
                {
308
0
                    split_length = l;
309
0
                    split_axis = i;
310
0
                }
311
0
            }
312
313
            // Now split the box into halves
314
0
            boxes.push_back (split_box->split (split_axis, poselem, vdata, vsz));
315
0
        }
316
317
        // Fine, now from every cluster choose the vertex that is most
318
        // distant from the geometrical center and from other extremes.
319
0
        for (const auto & boxe : boxes)
320
0
        {
321
0
            Real rating = 0;
322
0
            Vector3 best_vertex;
323
324
0
            for (unsigned int i : boxe.mIndices)
325
0
            {
326
0
                float *v;
327
0
                poselem->baseVertexPointerToElement (vdata + i * vsz, &v);
328
329
0
                Vector3 vv (v [0], v [1], v [2]);
330
0
                Real r = (vv - center).squaredLength ();
331
332
0
                for (auto extremityPoint : extremityPoints)
333
0
                    r += (extremityPoint - vv).squaredLength ();
334
0
                if (r > rating)
335
0
                {
336
0
                    rating = r;
337
0
                    best_vertex = vv;
338
0
                }
339
0
            }
340
341
0
            if (rating > 0)
342
0
                extremityPoints.push_back (best_vertex);
343
0
        }
344
345
0
        vbuf->unlock ();
346
0
    }
347
    //---------------------------------------------------------------------
348
    void SubMesh::setBuildEdgesEnabled(bool b)
349
0
    {
350
0
        mBuildEdgesEnabled = b;
351
0
        if(parent)
352
0
        {
353
0
            parent->freeEdgeList();
354
0
            parent->setAutoBuildEdgeLists(true);
355
0
        }
356
0
    }
357
    //---------------------------------------------------------------------
358
    SubMesh * SubMesh::clone(const String& newName, Mesh *parentMesh)
359
0
    {
360
        // This is a bit like a copy constructor, but with the additional aspect of registering the clone with
361
        //  the MeshManager
362
363
0
        if(parentMesh == NULL)
364
0
            parentMesh = parent;
365
366
0
        HardwareBufferManagerBase* bufferManager = parentMesh->getHardwareBufferManager();
367
0
        SubMesh* newSub = parentMesh->createSubMesh(newName);
368
369
0
        newSub->mMaterial = this->mMaterial;
370
0
        newSub->operationType = this->operationType;
371
0
        newSub->useSharedVertices = this->useSharedVertices;
372
0
        newSub->extremityPoints = this->extremityPoints;
373
374
0
        if (!this->useSharedVertices)
375
0
        {
376
            // Copy unique vertex data
377
0
            newSub->vertexData = this->vertexData->clone(true, bufferManager);
378
            // Copy unique index map
379
0
            newSub->blendIndexToBoneIndexMap = this->blendIndexToBoneIndexMap;
380
0
        }
381
382
        // Copy index data
383
0
        OGRE_DELETE newSub->indexData;
384
0
        newSub->indexData = this->indexData->clone(true, bufferManager);
385
        // Copy any bone assignments
386
0
        newSub->mBoneAssignments = this->mBoneAssignments;
387
0
        newSub->mBoneAssignmentsOutOfDate = this->mBoneAssignmentsOutOfDate;
388
389
        // Copy lod face lists
390
0
        newSub->mLodFaceList.reserve(this->mLodFaceList.size());
391
0
        SubMesh::LODFaceList::const_iterator facei;
392
0
        for (facei = this->mLodFaceList.begin(); facei != this->mLodFaceList.end(); ++facei) {
393
0
            IndexData* newIndexData = (*facei)->clone(true, bufferManager);
394
0
            newSub->mLodFaceList.push_back(newIndexData);
395
0
        }
396
0
        return newSub;
397
0
    }
398
}
399
400