Coverage Report

Created: 2025-08-29 06:18

/src/ogre/OgreMain/src/OgreInstanceBatchHW.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
#include "OgreInstanceBatchHW.h"
30
#include "OgreRenderOperation.h"
31
#include "OgreInstancedEntity.h"
32
33
namespace Ogre
34
{
35
    InstanceBatchHW::InstanceBatchHW( InstanceManager *creator, MeshPtr &meshReference,
36
                                        const MaterialPtr &material, size_t instancesPerBatch,
37
                                        const Mesh::IndexMap *indexToBoneMap, const String &batchName ) :
38
0
                InstanceBatch( creator, meshReference, material, instancesPerBatch,
39
0
                                indexToBoneMap, batchName ),
40
0
                mKeepStatic( false )
41
0
    {
42
        //Override defaults, so that InstancedEntities don't create a skeleton instance
43
0
        mTechnSupportsSkeletal = false;
44
0
    }
45
46
    InstanceBatchHW::~InstanceBatchHW()
47
0
    {
48
0
    }
49
50
    //-----------------------------------------------------------------------
51
    size_t InstanceBatchHW::calculateMaxNumInstances( const SubMesh *baseSubMesh, uint16 flags ) const
52
0
    {
53
0
        size_t retVal = 0;
54
55
0
        RenderSystem *renderSystem = Root::getSingleton().getRenderSystem();
56
0
        const RenderSystemCapabilities *capabilities = renderSystem->getCapabilities();
57
58
0
        if( capabilities->hasCapability( RSC_VERTEX_BUFFER_INSTANCE_DATA ) )
59
0
        {
60
            //This value is arbitrary (theorical max is 2^30 for D3D9) but is big enough and safe
61
0
            retVal = 65535;
62
0
        }
63
64
0
        return retVal;
65
0
    }
66
    //-----------------------------------------------------------------------
67
    void InstanceBatchHW::buildFrom( const SubMesh *baseSubMesh, const RenderOperation &renderOperation )
68
0
    {
69
0
        InstanceBatch::buildFrom( baseSubMesh, renderOperation );
70
71
        //We need to clone the VertexData (but just reference all buffers, except the last one)
72
        //because last buffer contains data specific to this batch, we need a different binding
73
0
        mRenderOperation.vertexData = mRenderOperation.vertexData->clone( false );
74
0
        mRemoveOwnVertexData = true;
75
0
        VertexData *thisVertexData      = mRenderOperation.vertexData;
76
0
        const unsigned short lastSource = thisVertexData->vertexDeclaration->getMaxSource();
77
0
        auto vertexBuffer = HardwareBufferManager::getSingleton().createVertexBuffer(
78
0
            thisVertexData->vertexDeclaration->getVertexSize(lastSource), mInstancesPerBatch, HBU_CPU_TO_GPU);
79
0
        thisVertexData->vertexBufferBinding->setBinding( lastSource, vertexBuffer );
80
0
        vertexBuffer->setIsInstanceData( true );
81
0
        vertexBuffer->setInstanceDataStepRate( 1 );
82
0
    }
83
    //-----------------------------------------------------------------------
84
    void InstanceBatchHW::setupVertices( const SubMesh* baseSubMesh )
85
0
    {
86
        //No skeletal animation support in this technique, sorry
87
0
        mRenderOperation.vertexData = baseSubMesh->vertexData->_cloneRemovingBlendData();
88
0
        mRemoveOwnVertexData = true; //Raise flag to remove our own vertex data in the end (not always needed)
89
        
90
0
        VertexData *thisVertexData = mRenderOperation.vertexData;
91
92
        //Modify the declaration so it contains an extra source, where we can put the per instance data
93
0
        size_t offset               = 0;
94
0
        unsigned short nextTexCoord = thisVertexData->vertexDeclaration->getNextFreeTextureCoordinate();
95
0
        const unsigned short newSource = thisVertexData->vertexDeclaration->getMaxSource() + 1;
96
0
        for (unsigned char i = 0; i < 3 + mCreator->getNumCustomParams(); ++i)
97
0
        {
98
0
            thisVertexData->vertexDeclaration->addElement( newSource, offset, VET_FLOAT4,
99
0
                                                            VES_TEXTURE_COORDINATES, nextTexCoord++ );
100
0
            offset = thisVertexData->vertexDeclaration->getVertexSize( newSource );
101
0
        }
102
103
        //Create the vertex buffer containing per instance data
104
0
        auto vertexBuffer = HardwareBufferManager::getSingleton().createVertexBuffer(
105
0
            thisVertexData->vertexDeclaration->getVertexSize(newSource), mInstancesPerBatch, HBU_CPU_TO_GPU);
106
0
        thisVertexData->vertexBufferBinding->setBinding( newSource, vertexBuffer );
107
0
        vertexBuffer->setIsInstanceData( true );
108
0
        vertexBuffer->setInstanceDataStepRate( 1 );
109
0
    }
110
    //-----------------------------------------------------------------------
111
    void InstanceBatchHW::setupIndices( const SubMesh* baseSubMesh )
112
0
    {
113
        //We could use just a reference, but the InstanceManager will in the end attampt to delete
114
        //the pointer, and we can't give it something that doesn't belong to us.
115
0
        mRenderOperation.indexData = baseSubMesh->indexData->clone( true );
116
0
        mRemoveOwnIndexData = true; //Raise flag to remove our own index data in the end (not always needed)
117
0
    }
118
    //-----------------------------------------------------------------------
119
    bool InstanceBatchHW::checkSubMeshCompatibility( const SubMesh* baseSubMesh )
120
0
    {
121
        //Max number of texture coordinates is _usually_ 8, we need at least 3 available
122
0
        if( baseSubMesh->vertexData->vertexDeclaration->getNextFreeTextureCoordinate() > 8-2 )
123
0
        {
124
0
            OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Given mesh must have at "
125
0
                                                        "least 3 free TEXCOORDs",
126
0
                        "InstanceBatchHW::checkSubMeshCompatibility");
127
0
        }
128
0
        if( baseSubMesh->vertexData->vertexDeclaration->getNextFreeTextureCoordinate() >
129
0
            8-2-mCreator->getNumCustomParams() ||
130
0
            3 + mCreator->getNumCustomParams() >= 8 )
131
0
        {
132
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "There are not enough free TEXCOORDs to hold the "
133
0
                                                        "custom parameters (required: " +
134
0
                                                        Ogre::StringConverter::toString( 3 + mCreator->
135
0
                                                        getNumCustomParams() ) + "). See InstanceManager"
136
0
                                                        "::setNumCustomParams documentation",
137
0
                        "InstanceBatchHW::checkSubMeshCompatibility");
138
0
        }
139
140
0
        return InstanceBatch::checkSubMeshCompatibility( baseSubMesh );
141
0
    }
142
    //-----------------------------------------------------------------------
143
    size_t InstanceBatchHW::updateVertexBuffer( Camera *currentCamera )
144
0
    {
145
0
        size_t retVal = 0;
146
147
        //Now lock the vertex buffer and copy the 4x3 matrices, only those who need it!
148
0
        VertexBufferBinding* binding = mRenderOperation.vertexData->vertexBufferBinding; 
149
0
        const ushort bufferIdx = ushort(binding->getBufferCount()-1);
150
0
        HardwareBufferLockGuard vertexLock(binding->getBuffer(bufferIdx), HardwareBuffer::HBL_DISCARD);
151
0
        float *pDest = static_cast<float*>(vertexLock.pData);
152
0
        unsigned char numCustomParams           = mCreator->getNumCustomParams();
153
0
        size_t customParamIdx                   = 0;
154
155
0
        for (auto *e : mInstancedEntities)
156
0
        {
157
            //Cull on an individual basis, the less entities are visible, the less instances we draw.
158
            //No need to use null matrices at all!
159
0
            if (e->findVisible(currentCamera ))
160
0
            {
161
0
                const size_t floatsWritten = e->getTransforms3x4( (Matrix3x4f*)pDest);
162
163
0
                if( mManager->getCameraRelativeRendering() )
164
0
                    makeMatrixCameraRelative3x4((Matrix3x4f*)pDest, floatsWritten / 12);
165
166
0
                pDest += floatsWritten;
167
168
                //Write custom parameters, if any
169
0
                for (unsigned char i = 0; i < numCustomParams; ++i)
170
0
                {
171
0
                    memcpy(pDest, mCustomParams[customParamIdx+i].ptr(), sizeof(Vector4f));
172
0
                    pDest += 4;
173
0
                }
174
0
                ++retVal;
175
0
            }
176
0
            customParamIdx += numCustomParams;
177
0
        }
178
179
0
        return retVal;
180
0
    }
181
    //-----------------------------------------------------------------------
182
    void InstanceBatchHW::_boundsDirty(void)
183
0
    {
184
        //Don't update if we're static, but still mark we're dirty
185
0
        if( !mBoundsDirty && !mKeepStatic )
186
0
            mCreator->_addDirtyBatch( this );
187
0
        mBoundsDirty = true;
188
0
    }
189
    //-----------------------------------------------------------------------
190
    void InstanceBatchHW::setStaticAndUpdate( bool bStatic )
191
0
    {
192
        //We were dirty but didn't update bounds. Do it now.
193
0
        if( mKeepStatic && mBoundsDirty )
194
0
            mCreator->_addDirtyBatch( this );
195
196
0
        mKeepStatic = bStatic;
197
0
        if( mKeepStatic )
198
0
        {
199
            //One final update, since there will be none from now on
200
            //(except further calls to this function). Pass NULL because
201
            //we want to include only those who were added to the scene
202
            //but we don't want to perform culling
203
0
            mRenderOperation.numberOfInstances = updateVertexBuffer( 0 );
204
0
        }
205
0
    }
206
    //-----------------------------------------------------------------------
207
    void InstanceBatchHW::getWorldTransforms( Matrix4* xform ) const
208
0
    {
209
0
        *xform = Matrix4::IDENTITY;
210
0
    }
211
    //-----------------------------------------------------------------------
212
    void InstanceBatchHW::_updateRenderQueue( RenderQueue* queue )
213
0
    {
214
0
        if( !mKeepStatic )
215
0
        {
216
            //Completely override base functionality, since we don't cull on an "all-or-nothing" basis
217
            //and we don't support skeletal animation
218
0
            if( (mRenderOperation.numberOfInstances = updateVertexBuffer( mCurrentCamera )) )
219
0
                queue->addRenderable( this, mRenderQueueID, mRenderQueuePriority );
220
0
        }
221
0
        else
222
0
        {
223
0
            OgreAssert(!mManager->getCameraRelativeRendering(),
224
0
                       "Camera-relative rendering is incompatible with Instancing's static batches. "
225
0
                       "Disable at least one of them");
226
227
            //Don't update when we're static
228
0
            if( mRenderOperation.numberOfInstances )
229
0
                queue->addRenderable( this, mRenderQueueID, mRenderQueuePriority );
230
0
        }
231
0
    }
232
}