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_VTF.h"
30 #include "OgreHardwarePixelBuffer.h"
31 #include "OgreInstancedEntity.h"
32 
33 namespace Ogre
34 {
35     static const uint16 c_maxTexWidthHW = 4096;
36     static const uint16 c_maxTexHeightHW    = 4096;
37 
InstanceBatchHW_VTF(InstanceManager * creator,MeshPtr & meshReference,const MaterialPtr & material,size_t instancesPerBatch,const Mesh::IndexMap * indexToBoneMap,const String & batchName)38     InstanceBatchHW_VTF::InstanceBatchHW_VTF(
39         InstanceManager *creator, MeshPtr &meshReference,
40         const MaterialPtr &material, size_t instancesPerBatch,
41         const Mesh::IndexMap *indexToBoneMap, const String &batchName )
42             : BaseInstanceBatchVTF( creator, meshReference, material,
43                                     instancesPerBatch, indexToBoneMap, batchName),
44               mKeepStatic( false )
45     {
46     }
47     //-----------------------------------------------------------------------
~InstanceBatchHW_VTF()48     InstanceBatchHW_VTF::~InstanceBatchHW_VTF()
49     {
50     }
51     //-----------------------------------------------------------------------
setupVertices(const SubMesh * baseSubMesh)52     void InstanceBatchHW_VTF::setupVertices( const SubMesh* baseSubMesh )
53     {
54         mRenderOperation.vertexData = OGRE_NEW VertexData();
55         mRemoveOwnVertexData = true; //Raise flag to remove our own vertex data in the end (not always needed)
56 
57         VertexData *thisVertexData = mRenderOperation.vertexData;
58         VertexData *baseVertexData = baseSubMesh->vertexData;
59 
60         thisVertexData->vertexStart = 0;
61         thisVertexData->vertexCount = baseVertexData->vertexCount;
62         mRenderOperation.numberOfInstances = mInstancesPerBatch;
63 
64         HardwareBufferManager::getSingleton().destroyVertexDeclaration(
65                                                                     thisVertexData->vertexDeclaration );
66         thisVertexData->vertexDeclaration = baseVertexData->vertexDeclaration->clone();
67 
68         //Reuse all vertex buffers
69         VertexBufferBinding::VertexBufferBindingMap::const_iterator itor = baseVertexData->
70                                                             vertexBufferBinding->getBindings().begin();
71         VertexBufferBinding::VertexBufferBindingMap::const_iterator end  = baseVertexData->
72                                                             vertexBufferBinding->getBindings().end();
73         while( itor != end )
74         {
75             const unsigned short bufferIdx = itor->first;
76             const HardwareVertexBufferSharedPtr vBuf = itor->second;
77             thisVertexData->vertexBufferBinding->setBinding( bufferIdx, vBuf );
78             ++itor;
79         }
80 
81         //Remove the blend weights & indices
82         HWBoneIdxVec hwBoneIdx;
83         HWBoneWgtVec hwBoneWgt;
84 
85         //Blend weights may not be present because HW_VTF does not require to be skeletally animated
86         const VertexElement *veWeights = baseVertexData->vertexDeclaration->
87                                                         findElementBySemantic( VES_BLEND_WEIGHTS );
88         if( veWeights )
89             mWeightCount = forceOneWeight() ? 1 : veWeights->getSize() / sizeof(float);
90         else
91             mWeightCount = 1;
92 
93         hwBoneIdx.resize( baseVertexData->vertexCount * mWeightCount, 0 );
94 
95         if( mMeshReference->hasSkeleton() && mMeshReference->getSkeleton() )
96         {
97             if(mWeightCount > 1)
98             {
99                 hwBoneWgt.resize( baseVertexData->vertexCount * mWeightCount, 0 );
100                 retrieveBoneIdxWithWeights(baseVertexData, hwBoneIdx, hwBoneWgt);
101             }
102             else
103             {
104                 retrieveBoneIdx( baseVertexData, hwBoneIdx );
105             }
106 
107             const VertexElement* pElement = thisVertexData->vertexDeclaration->findElementBySemantic
108                                                                                     (VES_BLEND_INDICES);
109             if (pElement)
110             {
111                 unsigned short skelDataSource = pElement->getSource();
112                 thisVertexData->vertexDeclaration->removeElement( VES_BLEND_INDICES );
113                 thisVertexData->vertexDeclaration->removeElement( VES_BLEND_WEIGHTS );
114                 if (thisVertexData->vertexDeclaration->findElementsBySource(skelDataSource).empty())
115                 {
116                     thisVertexData->vertexDeclaration->closeGapsInSource();
117                     thisVertexData->vertexBufferBinding->unsetBinding(skelDataSource);
118                     VertexBufferBinding::BindingIndexMap tmpMap;
119                     thisVertexData->vertexBufferBinding->closeGaps(tmpMap);
120                 }
121             }
122         }
123 
124         createVertexTexture( baseSubMesh );
125         createVertexSemantics( thisVertexData, baseVertexData, hwBoneIdx, hwBoneWgt);
126     }
127     //-----------------------------------------------------------------------
setupIndices(const SubMesh * baseSubMesh)128     void InstanceBatchHW_VTF::setupIndices( const SubMesh* baseSubMesh )
129     {
130         //We could use just a reference, but the InstanceManager will in the end attampt to delete
131         //the pointer, and we can't give it something that doesn't belong to us.
132         mRenderOperation.indexData = baseSubMesh->indexData->clone( true );
133         mRemoveOwnIndexData = true; //Raise flag to remove our own index data in the end (not always needed)
134     }
135     //-----------------------------------------------------------------------
createVertexSemantics(VertexData * thisVertexData,VertexData * baseVertexData,const HWBoneIdxVec & hwBoneIdx,const HWBoneWgtVec & hwBoneWgt)136     void InstanceBatchHW_VTF::createVertexSemantics( VertexData *thisVertexData,
137                                                          VertexData *baseVertexData,
138                                                          const HWBoneIdxVec &hwBoneIdx,
139                                                          const HWBoneWgtVec& hwBoneWgt)
140     {
141         const float texWidth  = static_cast<float>(mMatrixTexture->getWidth());
142 
143         //Only one weight per vertex is supported. It would not only be complex, but prohibitively slow.
144         //Put them in a new buffer, since it's 16 bytes aligned :-)
145         unsigned short newSource = thisVertexData->vertexDeclaration->getMaxSource() + 1;
146 
147         size_t offset = 0;
148 
149         size_t maxFloatsPerVector = 4;
150 
151         //Can fit two dual quaternions in every float4, but only one 3x4 matrix
152         for(size_t i = 0; i < mWeightCount; i += maxFloatsPerVector / mRowLength)
153         {
154             offset += thisVertexData->vertexDeclaration->addElement( newSource, offset, VET_FLOAT4, VES_TEXTURE_COORDINATES,
155                                         thisVertexData->vertexDeclaration->getNextFreeTextureCoordinate() ).getSize();
156         }
157 
158         //Add the weights (supports up to four, which is Ogre's limit)
159         if(mWeightCount > 1)
160         {
161             thisVertexData->vertexDeclaration->addElement(newSource, offset, VET_FLOAT4, VES_BLEND_WEIGHTS,
162                                         0 ).getSize();
163         }
164 
165         //Create our own vertex buffer
166         HardwareVertexBufferSharedPtr vertexBuffer =
167             HardwareBufferManager::getSingleton().createVertexBuffer(
168             thisVertexData->vertexDeclaration->getVertexSize(newSource),
169             thisVertexData->vertexCount,
170             HardwareBuffer::HBU_STATIC_WRITE_ONLY );
171         thisVertexData->vertexBufferBinding->setBinding( newSource, vertexBuffer );
172 
173         HardwareBufferLockGuard vertexLock(vertexBuffer, HardwareBuffer::HBL_DISCARD);
174         float *thisFloat = static_cast<float*>(vertexLock.pData);
175 
176         //Create the UVs to sample from the right bone/matrix
177         for( size_t j=0; j < baseVertexData->vertexCount * mWeightCount; j += mWeightCount)
178         {
179             size_t numberOfMatricesInLine = 0;
180 
181             //Write the matrices, adding padding as needed
182             for(size_t i = 0; i < mWeightCount; ++i)
183             {
184                 //Write the matrix
185                 for( size_t k=0; k < mRowLength; ++k)
186                 {
187                     //Only calculate U (not V) since all matrices are in the same row. We use the instanced
188                     //(repeated) buffer to tell how much U & V we need to offset
189                     size_t instanceIdx = hwBoneIdx[j+i] * mRowLength + k;
190                     *thisFloat++ = instanceIdx / texWidth;
191                 }
192 
193                 ++numberOfMatricesInLine;
194 
195                 //If another matrix can't be fit, we're on another line, or if this is the last weight
196                 if((numberOfMatricesInLine + 1) * mRowLength > maxFloatsPerVector || (i+1) == mWeightCount)
197                 {
198                     //Place zeroes in the remaining coordinates
199                     for ( size_t k=mRowLength * numberOfMatricesInLine; k < maxFloatsPerVector; ++k)
200                     {
201                         *thisFloat++ = 0.0f;
202                     }
203 
204                     numberOfMatricesInLine = 0;
205                 }
206             }
207 
208             //Don't need to write weights if there is only one
209             if(mWeightCount > 1)
210             {
211                 //Write the weights
212                 for(size_t i = 0; i < mWeightCount; ++i)
213                 {
214                     *thisFloat++ = hwBoneWgt[j+i];
215                 }
216 
217                 //Write the empty space
218                 for(size_t i = mWeightCount; i < maxFloatsPerVector; ++i)
219                 {
220                     *thisFloat++ = 0.0f;
221                 }
222             }
223         }
224 
225         vertexLock.unlock();
226 
227         //Now create the instance buffer that will be incremented per instance, contains UV offsets
228         newSource = thisVertexData->vertexDeclaration->getMaxSource() + 1;
229         offset = thisVertexData->vertexDeclaration->addElement( newSource, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES,
230                                     thisVertexData->vertexDeclaration->getNextFreeTextureCoordinate() ).getSize();
231         if (useBoneMatrixLookup())
232         {
233             //if using bone matrix lookup we will need to add 3 more float4 to contain the matrix. containing
234             //the personal world transform of each entity.
235             offset += thisVertexData->vertexDeclaration->addElement( newSource, offset, VET_FLOAT4, VES_TEXTURE_COORDINATES,
236                 thisVertexData->vertexDeclaration->getNextFreeTextureCoordinate() ).getSize();
237             offset += thisVertexData->vertexDeclaration->addElement( newSource, offset, VET_FLOAT4, VES_TEXTURE_COORDINATES,
238                 thisVertexData->vertexDeclaration->getNextFreeTextureCoordinate() ).getSize();
239             thisVertexData->vertexDeclaration->addElement( newSource, offset, VET_FLOAT4, VES_TEXTURE_COORDINATES,
240                 thisVertexData->vertexDeclaration->getNextFreeTextureCoordinate() ).getSize();
241             //Add two floats of padding here? or earlier?
242             //If not using bone matrix lookup, is it ok that it is 8 bytes since divides evenly into 16
243 
244         }
245 
246         //Create our own vertex buffer
247         mInstanceVertexBuffer = HardwareBufferManager::getSingleton().createVertexBuffer(
248                                         thisVertexData->vertexDeclaration->getVertexSize(newSource),
249                                         mInstancesPerBatch,
250                                         HardwareBuffer::HBU_STATIC_WRITE_ONLY );
251         thisVertexData->vertexBufferBinding->setBinding( newSource, mInstanceVertexBuffer );
252 
253         //Mark this buffer as instanced
254         mInstanceVertexBuffer->setIsInstanceData( true );
255         mInstanceVertexBuffer->setInstanceDataStepRate( 1 );
256 
257         updateInstanceDataBuffer(true, NULL);
258     }
259 
260     //updates the vertex buffer containing the per instance data
updateInstanceDataBuffer(bool isFirstTime,Camera * currentCamera)261     size_t InstanceBatchHW_VTF::updateInstanceDataBuffer(bool isFirstTime, Camera* currentCamera)
262     {
263         size_t visibleEntityCount = 0;
264         bool useMatrixLookup = useBoneMatrixLookup();
265         if (isFirstTime ^ useMatrixLookup)
266         {
267             //update the mTransformLookupNumber value in the entities if needed
268             updateSharedLookupIndexes();
269 
270             const float texWidth  = static_cast<float>(mMatrixTexture->getWidth());
271             const float texHeight = static_cast<float>(mMatrixTexture->getHeight());
272 
273             //Calculate the texel offsets to correct them offline
274             //Awkwardly enough, the offset is needed in OpenGL too
275             Vector2 texelOffsets;
276             //RenderSystem *renderSystem = Root::getSingleton().getRenderSystem();
277             texelOffsets.x = /*renderSystem->getHorizontalTexelOffset()*/ -0.5f / texWidth;
278             texelOffsets.y = /*renderSystem->getHorizontalTexelOffset()*/ -0.5f / texHeight;
279 
280             HardwareBufferLockGuard instanceVertexLock(mInstanceVertexBuffer, HardwareBuffer::HBL_DISCARD);
281             float *thisVec = static_cast<float*>(instanceVertexLock.pData);
282 
283             const size_t maxPixelsPerLine = std::min( static_cast<size_t>(mMatrixTexture->getWidth()), mMaxFloatsPerLine >> 2 );
284 
285             //Calculate UV offsets, which change per instance
286             for( size_t i=0; i<mInstancesPerBatch; ++i )
287             {
288                 InstancedEntity* entity = useMatrixLookup ? mInstancedEntities[i] : NULL;
289                 if  //Update if we are not using a lookup bone matrix method. In this case the function will
290                     //be called only once
291                     (!useMatrixLookup ||
292                     //Update if we are in the visible range of the camera (for look up bone matrix method
293                     //and static mode).
294                     (entity->findVisible(currentCamera)))
295                 {
296                     size_t matrixIndex = useMatrixLookup ? entity->mTransformLookupNumber : i;
297                     size_t instanceIdx = matrixIndex * mMatricesPerInstance * mRowLength;
298                     *thisVec = ((instanceIdx % maxPixelsPerLine) / texWidth) - (float)(texelOffsets.x);
299                     *(thisVec + 1) = ((instanceIdx / maxPixelsPerLine) / texHeight) - (float)(texelOffsets.y);
300                     thisVec += 2;
301 
302                     if (useMatrixLookup)
303                     {
304                         const Affine3& mat =  entity->_getParentNodeFullTransform();
305                         *(thisVec)     = static_cast<float>( mat[0][0] );
306                         *(thisVec + 1) = static_cast<float>( mat[0][1] );
307                         *(thisVec + 2) = static_cast<float>( mat[0][2] );
308                         *(thisVec + 3) = static_cast<float>( mat[0][3] );
309                         *(thisVec + 4) = static_cast<float>( mat[1][0] );
310                         *(thisVec + 5) = static_cast<float>( mat[1][1] );
311                         *(thisVec + 6) = static_cast<float>( mat[1][2] );
312                         *(thisVec + 7) = static_cast<float>( mat[1][3] );
313                         *(thisVec + 8) = static_cast<float>( mat[2][0] );
314                         *(thisVec + 9) = static_cast<float>( mat[2][1] );
315                         *(thisVec + 10)= static_cast<float>( mat[2][2] );
316                         *(thisVec + 11)= static_cast<float>( mat[2][3] );
317                         if(currentCamera && mManager->getCameraRelativeRendering()) // && useMatrixLookup
318                         {
319                             const Vector3 &cameraRelativePosition = currentCamera->getDerivedPosition();
320                             *(thisVec + 3) -= static_cast<float>( cameraRelativePosition.x );
321                             *(thisVec + 7) -= static_cast<float>( cameraRelativePosition.y );
322                             *(thisVec + 11) -=  static_cast<float>( cameraRelativePosition.z );
323                         }
324                         thisVec += 12;
325                     }
326                     ++visibleEntityCount;
327                 }
328             }
329         }
330         else
331         {
332             visibleEntityCount = mInstancedEntities.size();
333         }
334         return visibleEntityCount;
335     }
336 
337     //-----------------------------------------------------------------------
checkSubMeshCompatibility(const SubMesh * baseSubMesh)338     bool InstanceBatchHW_VTF::checkSubMeshCompatibility( const SubMesh* baseSubMesh )
339     {
340         //Max number of texture coordinates is _usually_ 8, we need at least 2 available
341         unsigned short neededTextureCoord = 2;
342         if (useBoneMatrixLookup())
343         {
344             //we need another 3 for the unique world transform of each instanced entity
345             neededTextureCoord += 3;
346         }
347         if( baseSubMesh->vertexData->vertexDeclaration->getNextFreeTextureCoordinate() > 8 - neededTextureCoord )
348         {
349             OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,
350                     String("Given mesh must have at least ") +
351                     StringConverter::toString(neededTextureCoord) + "free TEXCOORDs",
352                     "InstanceBatchHW_VTF::checkSubMeshCompatibility");
353         }
354 
355         return InstanceBatch::checkSubMeshCompatibility( baseSubMesh );
356     }
357     //-----------------------------------------------------------------------
calculateMaxNumInstances(const SubMesh * baseSubMesh,uint16 flags) const358     size_t InstanceBatchHW_VTF::calculateMaxNumInstances(
359                     const SubMesh *baseSubMesh, uint16 flags ) const
360     {
361         size_t retVal = 0;
362 
363         RenderSystem *renderSystem = Root::getSingleton().getRenderSystem();
364         const RenderSystemCapabilities *capabilities = renderSystem->getCapabilities();
365 
366         //VTF & HW Instancing must be supported
367         if( capabilities->hasCapability( RSC_VERTEX_BUFFER_INSTANCE_DATA ) &&
368             capabilities->hasCapability( RSC_VERTEX_TEXTURE_FETCH ) )
369         {
370             //TODO: Check PF_FLOAT32_RGBA is supported (should be, since it was the 1st one)
371             const size_t numBones = std::max<size_t>( 1, baseSubMesh->blendIndexToBoneIndexMap.size() );
372 
373             const size_t maxUsableWidth = c_maxTexWidthHW - (c_maxTexWidthHW % (numBones * mRowLength));
374 
375             //See InstanceBatchHW::calculateMaxNumInstances for the 65535
376             retVal = std::min<size_t>( 65535, maxUsableWidth * c_maxTexHeightHW / mRowLength / numBones );
377 
378             if( flags & IM_VTFBESTFIT )
379             {
380                 size_t numUsedSkeletons = mInstancesPerBatch;
381                 if (flags & IM_VTFBONEMATRIXLOOKUP)
382                     numUsedSkeletons = std::min<size_t>(getMaxLookupTableInstances(), numUsedSkeletons);
383                 const size_t instancesPerBatch = std::min( retVal, numUsedSkeletons );
384                 //Do the same as in createVertexTexture(), but changing c_maxTexWidthHW for maxUsableWidth
385                 const size_t numWorldMatrices = instancesPerBatch * numBones;
386 
387                 size_t texWidth  = std::min<size_t>( numWorldMatrices * mRowLength, maxUsableWidth );
388                 size_t texHeight = numWorldMatrices * mRowLength / maxUsableWidth;
389 
390                 const size_t remainder = (numWorldMatrices * mRowLength) % maxUsableWidth;
391 
392                 if( remainder && texHeight > 0 )
393                     retVal = static_cast<size_t>(texWidth * texHeight / (float)mRowLength / (float)(numBones));
394             }
395         }
396 
397         return retVal;
398     }
399     //-----------------------------------------------------------------------
updateVertexTexture(Camera * currentCamera)400     size_t InstanceBatchHW_VTF::updateVertexTexture( Camera *currentCamera )
401     {
402         size_t renderedInstances = 0;
403         bool useMatrixLookup = useBoneMatrixLookup();
404         if (useMatrixLookup)
405         {
406             //if we are using bone matrix look up we have to update the instance buffer for the
407             //vertex texture to be relevant
408 
409             //also note that in this case the number of instances to render comes directly from the
410             //updateInstanceDataBuffer() function, not from this function.
411             renderedInstances = updateInstanceDataBuffer(false, currentCamera);
412         }
413 
414 
415         mDirtyAnimation = false;
416 
417         //Now lock the texture and copy the 4x3 matrices!
418         HardwareBufferLockGuard matTexLock(mMatrixTexture->getBuffer(), HardwareBuffer::HBL_DISCARD);
419         const PixelBox &pixelBox = mMatrixTexture->getBuffer()->getCurrentLock();
420 
421         float *pSource = reinterpret_cast<float*>(pixelBox.data);
422 
423         InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
424 
425         std::vector<bool> writtenPositions(getMaxLookupTableInstances(), false);
426 
427         size_t floatPerEntity = mMatricesPerInstance * mRowLength * 4;
428         size_t entitiesPerPadding = (size_t)(mMaxFloatsPerLine / floatPerEntity);
429 
430         size_t instanceCount = mInstancedEntities.size();
431         size_t updatedInstances = 0;
432 
433         float* transforms = NULL;
434         //If using dual quaternions, write 3x4 matrices to a temporary buffer, then convert to dual quaternions
435         if(mUseBoneDualQuaternions)
436         {
437             transforms = mTempTransformsArray3x4;
438         }
439 
440         for(size_t i = 0 ; i < instanceCount ; ++i)
441         {
442             InstancedEntity* entity = mInstancedEntities[i];
443             size_t textureLookupPosition = updatedInstances;
444             if (useMatrixLookup)
445             {
446                 textureLookupPosition = entity->mTransformLookupNumber;
447             }
448             //Check that we are not using a lookup matrix or that we have not already written
449             //The bone data
450             if (((!useMatrixLookup) || !writtenPositions[entity->mTransformLookupNumber]) &&
451                 //Cull on an individual basis, the less entities are visible, the less instances we draw.
452                 //No need to use null matrices at all!
453                 (entity->findVisible( currentCamera )))
454             {
455                 float* pDest = pSource + floatPerEntity * textureLookupPosition +
456                     (size_t)(textureLookupPosition / entitiesPerPadding) * mWidthFloatsPadding;
457 
458                 if(!mUseBoneDualQuaternions)
459                 {
460                     transforms = pDest;
461                 }
462 
463                 if( mMeshReference->hasSkeleton() )
464                     mDirtyAnimation |= entity->_updateAnimation();
465 
466                 size_t floatsWritten = entity->getTransforms3x4( transforms );
467 
468                 if( !useMatrixLookup && mManager->getCameraRelativeRendering() )
469                     makeMatrixCameraRelative3x4( transforms, floatsWritten );
470 
471                 if(mUseBoneDualQuaternions)
472                 {
473                     convert3x4MatricesToDualQuaternions(transforms, floatsWritten / 12, pDest);
474                 }
475 
476                 if (useMatrixLookup)
477                 {
478                     writtenPositions[entity->mTransformLookupNumber] = true;
479                 }
480                 else
481                 {
482                     ++updatedInstances;
483                 }
484             }
485 
486             ++itor;
487         }
488 
489         if (!useMatrixLookup)
490         {
491             renderedInstances = updatedInstances;
492         }
493 
494         return renderedInstances;
495     }
496     //-----------------------------------------------------------------------
_boundsDirty(void)497     void InstanceBatchHW_VTF::_boundsDirty(void)
498     {
499         //Don't update if we're static, but still mark we're dirty
500         if( !mBoundsDirty && !mKeepStatic && mCreator)
501             mCreator->_addDirtyBatch( this );
502         mBoundsDirty = true;
503     }
504     //-----------------------------------------------------------------------
setStaticAndUpdate(bool bStatic)505     void InstanceBatchHW_VTF::setStaticAndUpdate( bool bStatic )
506     {
507         //We were dirty but didn't update bounds. Do it now.
508         if( mKeepStatic && mBoundsDirty )
509             mCreator->_addDirtyBatch( this );
510 
511         mKeepStatic = bStatic;
512         if( mKeepStatic )
513         {
514             //One final update, since there will be none from now on
515             //(except further calls to this function). Pass NULL because
516             //we want to include only those who were added to the scene
517             //but we don't want to perform culling
518             mRenderOperation.numberOfInstances = updateVertexTexture( 0 );
519         }
520     }
521     //-----------------------------------------------------------------------
_updateRenderQueue(RenderQueue * queue)522     void InstanceBatchHW_VTF::_updateRenderQueue( RenderQueue* queue )
523     {
524         if( !mKeepStatic )
525         {
526             //Completely override base functionality, since we don't cull on an "all-or-nothing" basis
527             if( (mRenderOperation.numberOfInstances = updateVertexTexture( mCurrentCamera )) )
528                 queue->addRenderable( this, mRenderQueueID, mRenderQueuePriority );
529         }
530         else
531         {
532             if( mManager->getCameraRelativeRendering() )
533             {
534                 OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "Camera-relative rendering is incompatible"
535                     " with Instancing's static batches. Disable at least one of them",
536                     "InstanceBatch::_updateRenderQueue");
537             }
538 
539             //Don't update when we're static
540             if( mRenderOperation.numberOfInstances )
541                 queue->addRenderable( this, mRenderQueueID, mRenderQueuePriority );
542         }
543     }
544 }
545