Coverage Report

Created: 2025-08-29 06:18

/src/ogre/OgreMain/src/OgreSubEntity.cpp
Line
Count
Source (jump to first uncovered line)
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
    SubEntity::SubEntity (Entity* parent, SubMesh* subMeshBasis)
33
0
        : Renderable(), mParentEntity(parent),
34
0
        mSubMesh(subMeshBasis), mCachedCamera(0)
35
0
    {
36
0
        mVisible = true;
37
0
        mRenderQueueID = 0;
38
0
        mRenderQueueIDSet = false;
39
0
        mRenderQueuePrioritySet = false;
40
0
        mSkelAnimVertexData = 0;
41
0
        mVertexAnimationAppliedThisFrame = false;
42
0
        mHardwarePoseCount = 0;
43
0
        mIndexStart = 0;
44
0
        mIndexEnd = 0;
45
0
        setMaterial(MaterialManager::getSingleton().getDefaultMaterial());
46
0
    }
47
0
    SubEntity::~SubEntity() = default; // ensure unique_ptr destructors are in cpp
48
    //-----------------------------------------------------------------------
49
    const String& SubEntity::getMaterialName(void) const
50
0
    {
51
0
        return mMaterialPtr ? mMaterialPtr->getName() : BLANKSTRING;
52
0
    }
53
    //-----------------------------------------------------------------------
54
    void SubEntity::setMaterialName( const String& name, const String& groupName /* = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME */)
55
0
    {
56
0
        MaterialPtr material = MaterialManager::getSingleton().getByName(name, groupName);
57
58
0
        if( !material )
59
0
        {
60
0
            logMaterialNotFound(name, groupName, "SubEntity of", mParentEntity->getName());
61
0
            material = MaterialManager::getSingleton().getDefaultMaterial();
62
0
        }
63
64
0
        setMaterial( material );
65
0
    }
66
    //-----------------------------------------------------------------------
67
    void SubEntity::setMaterial( const MaterialPtr& material )
68
0
    {
69
0
        mMaterialPtr = material;
70
        
71
0
        if (!mMaterialPtr)
72
0
        {
73
0
            LogManager::getSingleton().logError("Can't assign nullptr material "
74
0
                "to SubEntity of '" + mParentEntity->getName() + "'. Falling back to default");
75
            
76
0
            mMaterialPtr = MaterialManager::getSingleton().getDefaultMaterial();
77
0
        }
78
        
79
        // Ensure new material loaded (will not load again if already loaded)
80
0
        mMaterialPtr->load();
81
82
        // tell parent to reconsider material vertex processing options
83
0
        mParentEntity->reevaluateVertexProcessing();
84
0
    }
85
    //-----------------------------------------------------------------------
86
    void SubEntity::getRenderOperation(RenderOperation& op)
87
0
    {
88
        // Use LOD
89
0
        mSubMesh->_getRenderOperation(op, mParentEntity->mMeshLodIndex);
90
        // Deal with any vertex data overrides
91
0
        op.vertexData = getVertexDataForBinding();
92
93
        // If we use custom index position the client is responsible to set meaningful values 
94
0
        if(mIndexStart != mIndexEnd)
95
0
        {
96
0
            op.indexData->indexStart = mIndexStart;
97
0
            op.indexData->indexCount = mIndexEnd;
98
0
        }
99
0
    }
100
    //-----------------------------------------------------------------------
101
    void SubEntity::setIndexDataStartIndex(uint32 start_index)
102
0
    {
103
0
        if(start_index < mSubMesh->indexData->indexCount)
104
0
            mIndexStart = start_index;
105
0
    }
106
    //-----------------------------------------------------------------------
107
    void SubEntity::setIndexDataEndIndex(uint32 end_index)
108
0
    {
109
0
        if(end_index <= mSubMesh->indexData->indexCount)
110
0
            mIndexEnd = end_index;
111
0
    }
112
    //-----------------------------------------------------------------------
113
    void SubEntity::resetIndexDataStartEndIndex()
114
0
    {
115
0
        mIndexStart = 0;
116
0
        mIndexEnd = 0;
117
0
    }
118
    //-----------------------------------------------------------------------
119
    VertexData* SubEntity::getVertexDataForBinding(void)
120
0
    {
121
0
        if (mSubMesh->useSharedVertices)
122
0
        {
123
0
            return mParentEntity->getVertexDataForBinding();
124
0
        }
125
0
        else
126
0
        {
127
0
            Entity::VertexDataBindChoice c = 
128
0
                mParentEntity->chooseVertexDataForBinding(
129
0
                    mSubMesh->getVertexAnimationType() != VAT_NONE);
130
0
            switch(c)
131
0
            {
132
0
            case Entity::BIND_ORIGINAL:
133
0
                return mSubMesh->vertexData;
134
0
            case Entity::BIND_HARDWARE_MORPH:
135
0
                return mHardwareVertexAnimVertexData.get();
136
0
            case Entity::BIND_SOFTWARE_MORPH:
137
0
                return mSoftwareVertexAnimVertexData.get();
138
0
            case Entity::BIND_SOFTWARE_SKELETAL:
139
0
                return mSkelAnimVertexData.get();
140
0
            };
141
            // keep compiler happy
142
0
            return mSubMesh->vertexData;
143
144
0
        }
145
0
    }
146
    //-----------------------------------------------------------------------
147
    void SubEntity::getWorldTransforms(Matrix4* xform) const
148
0
    {
149
0
        if (!mParentEntity->mNumBoneMatrices ||
150
0
            !mParentEntity->isHardwareAnimationEnabled())
151
0
        {
152
            // No skeletal animation, or software skinning
153
0
            *xform = mParentEntity->_getParentNodeFullTransform();
154
0
        }
155
0
        else
156
0
        {
157
            // Hardware skinning, pass all actually used matrices
158
0
            const Mesh::IndexMap& indexMap = mSubMesh->useSharedVertices ?
159
0
                mSubMesh->parent->sharedBlendIndexToBoneIndexMap : mSubMesh->blendIndexToBoneIndexMap;
160
0
            assert(indexMap.size() <= mParentEntity->mNumBoneMatrices);
161
162
0
            if (MeshManager::getBonesUseObjectSpace())
163
0
            {
164
0
                *xform++ = mParentEntity->_getParentNodeFullTransform();
165
0
            }
166
167
0
            if (mParentEntity->_isSkeletonAnimated())
168
0
            {
169
                // Bones, use cached matrices built when Entity::_updateRenderQueue was called
170
0
                auto boneMatrices = MeshManager::getBonesUseObjectSpace() ? mParentEntity->mBoneMatrices
171
0
                                                                          : mParentEntity->mBoneWorldMatrices;
172
0
                assert(boneMatrices);
173
174
0
                for (auto idx : indexMap)
175
0
                {
176
0
                    *xform++ = boneMatrices[idx];
177
0
                }
178
0
            }
179
0
            else
180
0
            {
181
0
                auto& value = MeshManager::getBonesUseObjectSpace() ? Affine3::IDENTITY
182
0
                                                                    : mParentEntity->_getParentNodeFullTransform();
183
                // All animations disabled, use parent entity world transform only
184
0
                std::fill_n(xform, indexMap.size(), value);
185
0
            }
186
0
        }
187
0
    }
188
    //-----------------------------------------------------------------------
189
    unsigned short SubEntity::getNumWorldTransforms(void) const
190
0
    {
191
0
        if (!mParentEntity->mNumBoneMatrices ||
192
0
            !mParentEntity->isHardwareAnimationEnabled())
193
0
        {
194
            // No skeletal animation, or software skinning
195
0
            return 1;
196
0
        }
197
0
        else
198
0
        {
199
            // Hardware skinning, pass all actually used matrices
200
0
            const Mesh::IndexMap& indexMap = mSubMesh->useSharedVertices ?
201
0
                mSubMesh->parent->sharedBlendIndexToBoneIndexMap : mSubMesh->blendIndexToBoneIndexMap;
202
0
            assert(indexMap.size() <= mParentEntity->mNumBoneMatrices);
203
204
0
            return uint16(indexMap.size()) + uint16(MeshManager::getBonesUseObjectSpace());
205
0
        }
206
0
    }
207
    //-----------------------------------------------------------------------
208
    Real SubEntity::getSquaredViewDepth(const Camera* cam) const
209
0
    {
210
        // First of all, check the cached value
211
        // NB this is manually invalidated by parent each _notifyCurrentCamera call
212
        // Done this here rather than there since we only need this for transparent objects
213
0
        if (mCachedCamera == cam)
214
0
            return mCachedCameraDist;
215
216
0
        Node* n = mParentEntity->getParentNode();
217
0
        assert(n);
218
0
        Real dist;
219
0
        if (!mSubMesh->extremityPoints.empty())
220
0
        {
221
0
            bool euclidean = cam->getSortMode() == SM_DISTANCE;
222
0
            Vector3 zAxis = cam->getDerivedDirection();
223
0
            const Vector3 &cp = cam->getDerivedPosition();
224
0
            const Affine3 &l2w = mParentEntity->_getParentNodeFullTransform();
225
0
            dist = std::numeric_limits<Real>::infinity();
226
0
            for (const Vector3& v : mSubMesh->extremityPoints)
227
0
            {
228
0
                Vector3 diff = l2w * v - cp;
229
0
                Real d = euclidean ? diff.squaredLength() : Math::Sqr(zAxis.dotProduct(diff));
230
231
0
                dist = std::min(d, dist);
232
0
            }
233
0
        }
234
0
        else
235
0
            dist = n->getSquaredViewDepth(cam);
236
237
0
        mCachedCameraDist = dist;
238
0
        mCachedCamera = cam;
239
240
0
        return dist;
241
0
    }
242
    //-----------------------------------------------------------------------
243
    const LightList& SubEntity::getLights(void) const
244
0
    {
245
0
        return mParentEntity->queryLights();
246
0
    }
247
    //-----------------------------------------------------------------------
248
    void SubEntity::setVisible(bool visible)
249
0
    {
250
0
        mVisible = visible;
251
0
    }
252
    //-----------------------------------------------------------------------
253
    void SubEntity::prepareTempBlendBuffers(void)
254
0
    {
255
0
        if (mSubMesh->useSharedVertices)
256
0
            return;
257
258
0
        mSkelAnimVertexData.reset();
259
0
        mSoftwareVertexAnimVertexData.reset();
260
0
        mHardwareVertexAnimVertexData.reset();
261
262
0
        if (!mSubMesh->useSharedVertices)
263
0
        {
264
0
            if (mSubMesh->getVertexAnimationType() != VAT_NONE)
265
0
            {
266
                // Create temporary vertex blend info
267
                // Prepare temp vertex data if needed
268
                // Clone without copying data, don't remove any blending info
269
                // (since if we skeletally animate too, we need it)
270
0
                mSoftwareVertexAnimVertexData.reset(mSubMesh->vertexData->clone(false));
271
0
                mTempVertexAnimInfo.extractFrom(mSoftwareVertexAnimVertexData.get());
272
273
                // Also clone for hardware usage, don't remove blend info since we'll
274
                // need it if we also hardware skeletally animate
275
0
                mHardwareVertexAnimVertexData.reset(mSubMesh->vertexData->clone(false));
276
0
            }
277
278
0
            if (mParentEntity->hasSkeleton())
279
0
            {
280
                // Create temporary vertex blend info
281
                // Prepare temp vertex data if needed
282
                // Clone without copying data, remove blending info
283
                // (since blend is performed in software)
284
0
                mSkelAnimVertexData.reset(mSubMesh->vertexData->_cloneRemovingBlendData());
285
0
                mTempSkelAnimInfo.extractFrom(mSkelAnimVertexData.get());
286
0
            }
287
0
        }
288
0
    }
289
    //-----------------------------------------------------------------------
290
    bool SubEntity::getCastsShadows(void) const
291
0
    {
292
0
        return mParentEntity->getCastShadows();
293
0
    }
294
    //-----------------------------------------------------------------------
295
    VertexData* SubEntity::_getSkelAnimVertexData(void) 
296
0
    {
297
0
        assert (mSkelAnimVertexData && "Not software skinned or has no dedicated geometry!");
298
0
        return mSkelAnimVertexData.get();
299
0
    }
300
    //-----------------------------------------------------------------------
301
    VertexData* SubEntity::_getSoftwareVertexAnimVertexData(void)
302
0
    {
303
0
        assert (mSoftwareVertexAnimVertexData && "Not vertex animated or has no dedicated geometry!");
304
0
        return mSoftwareVertexAnimVertexData.get();
305
0
    }
306
    //-----------------------------------------------------------------------
307
    VertexData* SubEntity::_getHardwareVertexAnimVertexData(void)
308
0
    {
309
0
        assert (mHardwareVertexAnimVertexData && "Not vertex animated or has no dedicated geometry!");
310
0
        return mHardwareVertexAnimVertexData.get();
311
0
    }
312
    //-----------------------------------------------------------------------
313
    void SubEntity::_updateCustomGpuParameter(
314
        const GpuProgramParameters::AutoConstantEntry& constantEntry,
315
        GpuProgramParameters* params) const
316
0
    {
317
0
        if (constantEntry.paramType == GpuProgramParameters::ACT_ANIMATION_PARAMETRIC)
318
0
        {
319
            // Set up to 4 values, or up to limit of hardware animation entries
320
            // Pack into 4-element constants offset based on constant data index
321
            // If there are more than 4 entries, this will be called more than once
322
0
            Vector4 val(0.0f,0.0f,0.0f,0.0f);
323
0
            const auto& vd = mHardwareVertexAnimVertexData ? mHardwareVertexAnimVertexData : mParentEntity->mHardwareVertexAnimVertexData;
324
            
325
0
            size_t animIndex = constantEntry.data * 4;
326
0
            for (size_t i = 0; i < 4 && 
327
0
                animIndex < vd->hwAnimationDataList.size();
328
0
                ++i, ++animIndex)
329
0
            {
330
0
                val[i] = 
331
0
                    vd->hwAnimationDataList[animIndex].parametric;
332
0
            }
333
            // set the parametric morph value
334
0
            params->_writeRawConstant(constantEntry.physicalIndex, val);
335
0
        }
336
0
        else
337
0
        {
338
            // default
339
0
            return Renderable::_updateCustomGpuParameter(constantEntry, params);
340
0
        }
341
0
    }
342
    //-----------------------------------------------------------------------------
343
    void SubEntity::_markBuffersUnusedForAnimation(void)
344
0
    {
345
0
        mVertexAnimationAppliedThisFrame = false;
346
0
    }
347
    //-----------------------------------------------------------------------------
348
    void SubEntity::_markBuffersUsedForAnimation(void)
349
0
    {
350
0
        mVertexAnimationAppliedThisFrame = true;
351
0
    }
352
    //-----------------------------------------------------------------------------
353
    void SubEntity::_restoreBuffersForUnusedAnimation(bool hardwareAnimation)
354
0
    {
355
        // Rebind original positions if:
356
        //  We didn't apply any animation and 
357
        //    We're morph animated (hardware binds keyframe, software is missing)
358
        //    or we're pose animated and software (hardware is fine, still bound)
359
0
        if (mSubMesh->getVertexAnimationType() != VAT_NONE && 
360
0
            !mSubMesh->useSharedVertices && 
361
0
            !mVertexAnimationAppliedThisFrame &&
362
0
            (!hardwareAnimation || mSubMesh->getVertexAnimationType() == VAT_MORPH))
363
0
        {
364
            // Note, VES_POSITION is specified here but if normals are included in animation
365
            // then these will be re-bound too (buffers must be shared)
366
0
            const VertexElement* srcPosElem = 
367
0
                mSubMesh->vertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
368
0
            HardwareVertexBufferSharedPtr srcBuf = 
369
0
                mSubMesh->vertexData->vertexBufferBinding->getBuffer(
370
0
                srcPosElem->getSource());
371
372
            // Bind to software
373
0
            const VertexElement* destPosElem = 
374
0
                mSoftwareVertexAnimVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
375
0
            mSoftwareVertexAnimVertexData->vertexBufferBinding->setBinding(
376
0
                destPosElem->getSource(), srcBuf);
377
            
378
0
        }
379
380
        // rebind any missing hardware pose buffers
381
        // Caused by not having any animations enabled, or keyframes which reference
382
        // no poses
383
0
        if (!mSubMesh->useSharedVertices && hardwareAnimation 
384
0
            && mSubMesh->getVertexAnimationType() == VAT_POSE)
385
0
        {
386
0
            mParentEntity->bindMissingHardwarePoseBuffers(
387
0
                mSubMesh->vertexData, mHardwareVertexAnimVertexData.get());
388
0
        }
389
390
0
    }
391
    //-----------------------------------------------------------------------
392
    void SubEntity::setRenderQueueGroup(uint8 queueID)
393
0
    {
394
0
        mRenderQueueIDSet = true;
395
0
        mRenderQueueID = queueID;
396
0
    }
397
    //-----------------------------------------------------------------------
398
    void SubEntity::setRenderQueueGroupAndPriority(uint8 queueID, ushort priority)
399
0
    {
400
0
        setRenderQueueGroup(queueID);
401
0
        mRenderQueuePrioritySet = true;
402
0
        mRenderQueuePriority = priority;
403
0
    }
404
    //-----------------------------------------------------------------------
405
}