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 "OgreInstanceManager.h"
30 #include "OgreInstanceBatch.h"
31 #include "OgreInstancedEntity.h"
32 #include "OgreRenderQueue.h"
33 #include "OgreLodListener.h"
34 
35 namespace Ogre
36 {
InstanceBatch(InstanceManager * creator,MeshPtr & meshReference,const MaterialPtr & material,size_t instancesPerBatch,const Mesh::IndexMap * indexToBoneMap,const String & batchName)37     InstanceBatch::InstanceBatch( InstanceManager *creator, MeshPtr &meshReference,
38                                     const MaterialPtr &material, size_t instancesPerBatch,
39                                     const Mesh::IndexMap *indexToBoneMap, const String &batchName ) :
40                 Renderable(),
41                 MovableObject(),
42                 mInstancesPerBatch( instancesPerBatch ),
43                 mCreator( creator ),
44                 mMaterial( material ),
45                 mMeshReference( meshReference ),
46                 mIndexToBoneMap( indexToBoneMap ),
47                 mBoundingRadius( 0 ),
48                 mBoundsDirty( false ),
49                 mBoundsUpdated( false ),
50                 mCurrentCamera( 0 ),
51                 mMaterialLodIndex( 0 ),
52                 mDirtyAnimation(true),
53                 mTechnSupportsSkeletal( true ),
54                 mCachedCamera( 0 ),
55                 mTransformSharingDirty(true),
56                 mRemoveOwnVertexData(false),
57                 mRemoveOwnIndexData(false)
58     {
59         assert( mInstancesPerBatch );
60 
61         //Force batch visibility to be always visible. The instanced entities
62         //have individual visibility flags. If none matches the scene's current,
63         //then this batch won't rendered.
64         mVisibilityFlags = std::numeric_limits<Ogre::uint32>::max();
65 
66         if( indexToBoneMap )
67         {
68             assert( !(meshReference->hasSkeleton() && indexToBoneMap->empty()) );
69         }
70 
71         mFullBoundingBox.setExtents( -Vector3::ZERO, Vector3::ZERO );
72 
73         mName = batchName;
74 		if (mCreator != NULL)
75 		{
76 		    mCustomParams.resize( mCreator->getNumCustomParams() * mInstancesPerBatch, Ogre::Vector4::ZERO );
77 	    }
78 
79 	}
80 
~InstanceBatch()81     InstanceBatch::~InstanceBatch()
82     {
83         deleteAllInstancedEntities();
84 
85         //Remove the parent scene node automatically
86         SceneNode *sceneNode = getParentSceneNode();
87         if( sceneNode )
88         {
89             sceneNode->detachAllObjects();
90             sceneNode->getParentSceneNode()->removeAndDestroyChild( sceneNode );
91         }
92 
93         if( mRemoveOwnVertexData )
94             OGRE_DELETE mRenderOperation.vertexData;
95         if( mRemoveOwnIndexData )
96             OGRE_DELETE mRenderOperation.indexData;
97 
98     }
99 
_setInstancesPerBatch(size_t instancesPerBatch)100     void InstanceBatch::_setInstancesPerBatch( size_t instancesPerBatch )
101     {
102         if( !mInstancedEntities.empty() )
103         {
104             OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "Instances per batch can only be changed before"
105                         " building the batch.", "InstanceBatch::_setInstancesPerBatch");
106         }
107 
108         mInstancesPerBatch = instancesPerBatch;
109     }
110     //-----------------------------------------------------------------------
checkSubMeshCompatibility(const SubMesh * baseSubMesh)111     bool InstanceBatch::checkSubMeshCompatibility( const SubMesh* baseSubMesh )
112     {
113         if( baseSubMesh->operationType != RenderOperation::OT_TRIANGLE_LIST )
114         {
115             OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Only meshes with OT_TRIANGLE_LIST are supported",
116                         "InstanceBatch::checkSubMeshCompatibility");
117         }
118 
119         if( !mCustomParams.empty() && mCreator->getInstancingTechnique() != InstanceManager::HWInstancingBasic )
120         {
121             //Implementing this for ShaderBased is impossible. All other variants can be.
122             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Custom parameters not supported for this "
123                                                         "technique. Do you dare implementing it?"
124                                                         "See InstanceManager::setNumCustomParams "
125                                                         "documentation.",
126                         "InstanceBatch::checkSubMeshCompatibility");
127         }
128 
129         return true;
130     }
131     //-----------------------------------------------------------------------
_updateBounds(void)132     void InstanceBatch::_updateBounds(void)
133     {
134         mFullBoundingBox.setNull();
135 
136         InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
137         InstancedEntityVec::const_iterator end  = mInstancedEntities.end();
138 
139         Real maxScale = 0;
140         while( itor != end )
141         {
142             InstancedEntity* ent = (*itor);
143             //Only increase the bounding box for those objects we know are in the scene
144             if( ent->isInScene() )
145             {
146                 maxScale = std::max(maxScale, ent->getMaxScaleCoef());
147                 mFullBoundingBox.merge( ent->_getDerivedPosition() );
148             }
149 
150             ++itor;
151         }
152 
153         Real addToBound = maxScale * _getMeshReference()->getBoundingSphereRadius();
154         mFullBoundingBox.setMaximum(mFullBoundingBox.getMaximum() + addToBound);
155         mFullBoundingBox.setMinimum(mFullBoundingBox.getMinimum() - addToBound);
156 
157 
158         mBoundingRadius = Math::boundingRadiusFromAABBCentered( mFullBoundingBox );
159         if (mParentNode) {
160             mParentNode->needUpdate();
161         }
162 	mBoundsUpdated  = true;
163         mBoundsDirty    = false;
164     }
165 
166     //-----------------------------------------------------------------------
updateVisibility(void)167     void InstanceBatch::updateVisibility(void)
168     {
169         mVisible = false;
170 
171         InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
172         InstancedEntityVec::const_iterator end  = mInstancedEntities.end();
173 
174         while( itor != end && !mVisible )
175         {
176             //Trick to force Ogre not to render us if none of our instances is visible
177             //Because we do Camera::isVisible(), it is better if the SceneNode from the
178             //InstancedEntity is not part of the scene graph (i.e. ultimate parent is root node)
179             //to avoid unnecessary wasteful calculations
180             mVisible |= (*itor)->findVisible( mCurrentCamera );
181             ++itor;
182         }
183     }
184     //-----------------------------------------------------------------------
createAllInstancedEntities()185     void InstanceBatch::createAllInstancedEntities()
186     {
187         mInstancedEntities.reserve( mInstancesPerBatch );
188         mUnusedEntities.reserve( mInstancesPerBatch );
189 
190         for( size_t i=0; i<mInstancesPerBatch; ++i )
191         {
192             InstancedEntity *instance = generateInstancedEntity(i);
193             mInstancedEntities.push_back( instance );
194             mUnusedEntities.push_back( instance );
195         }
196     }
197     //-----------------------------------------------------------------------
generateInstancedEntity(size_t num)198     InstancedEntity* InstanceBatch::generateInstancedEntity(size_t num)
199     {
200         return OGRE_NEW InstancedEntity(this, static_cast<uint32>(num));
201     }
202     //-----------------------------------------------------------------------
deleteAllInstancedEntities()203     void InstanceBatch::deleteAllInstancedEntities()
204     {
205         InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
206         InstancedEntityVec::const_iterator end  = mInstancedEntities.end();
207 
208         while( itor != end )
209         {
210             if( (*itor)->getParentSceneNode() )
211                 (*itor)->getParentSceneNode()->detachObject( (*itor) );
212 
213             OGRE_DELETE *itor++;
214         }
215     }
216     //-----------------------------------------------------------------------
deleteUnusedInstancedEntities()217     void InstanceBatch::deleteUnusedInstancedEntities()
218     {
219         InstancedEntityVec::const_iterator itor = mUnusedEntities.begin();
220         InstancedEntityVec::const_iterator end  = mUnusedEntities.end();
221 
222         while( itor != end )
223             OGRE_DELETE *itor++;
224 
225         mUnusedEntities.clear();
226     }
227     //-----------------------------------------------------------------------
makeMatrixCameraRelative3x4(float * mat3x4,size_t numFloats)228     void InstanceBatch::makeMatrixCameraRelative3x4( float *mat3x4, size_t numFloats )
229     {
230         const Vector3 &cameraRelativePosition = mCurrentCamera->getDerivedPosition();
231 
232         for( size_t i=0; i<numFloats >> 2; i += 3 )
233         {
234             const Vector3 worldTrans( mat3x4[(i+0) * 4 + 3], mat3x4[(i+1) * 4 + 3],
235                                         mat3x4[(i+2) * 4 + 3] );
236             const Vector3 newPos( worldTrans - cameraRelativePosition );
237 
238             mat3x4[(i+0) * 4 + 3] = (float)newPos.x;
239             mat3x4[(i+1) * 4 + 3] = (float)newPos.y;
240             mat3x4[(i+2) * 4 + 3] = (float)newPos.z;
241         }
242     }
243     //-----------------------------------------------------------------------
build(const SubMesh * baseSubMesh)244     RenderOperation InstanceBatch::build( const SubMesh* baseSubMesh )
245     {
246         if( checkSubMeshCompatibility( baseSubMesh ) )
247         {
248             //Only triangle list at the moment
249             mRenderOperation.operationType  = RenderOperation::OT_TRIANGLE_LIST;
250             mRenderOperation.srcRenderable  = this;
251             mRenderOperation.useIndexes = true;
252             setupVertices( baseSubMesh );
253             setupIndices( baseSubMesh );
254 
255             createAllInstancedEntities();
256         }
257 
258         return mRenderOperation;
259     }
260     //-----------------------------------------------------------------------
buildFrom(const SubMesh * baseSubMesh,const RenderOperation & renderOperation)261     void InstanceBatch::buildFrom( const SubMesh *baseSubMesh, const RenderOperation &renderOperation )
262     {
263         mRenderOperation = renderOperation;
264         createAllInstancedEntities();
265     }
266     //-----------------------------------------------------------------------
createInstancedEntity()267     InstancedEntity* InstanceBatch::createInstancedEntity()
268     {
269         InstancedEntity *retVal = 0;
270 
271         if( !mUnusedEntities.empty() )
272         {
273             retVal = mUnusedEntities.back();
274             mUnusedEntities.pop_back();
275 
276             retVal->setInUse(true);
277         }
278 
279         return retVal;
280     }
281     //-----------------------------------------------------------------------
removeInstancedEntity(InstancedEntity * instancedEntity)282     void InstanceBatch::removeInstancedEntity( InstancedEntity *instancedEntity )
283     {
284         if( instancedEntity->mBatchOwner != this )
285         {
286             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
287                         "Trying to remove an InstancedEntity from scene created"
288                         " with a different InstanceBatch",
289                         "InstanceBatch::removeInstancedEntity()");
290         }
291         if( !instancedEntity->isInUse() )
292         {
293             OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
294                         "Trying to remove an InstancedEntity that is already removed!",
295                         "InstanceBatch::removeInstancedEntity()");
296         }
297 
298         if( instancedEntity->getParentSceneNode() )
299             instancedEntity->getParentSceneNode()->detachObject( instancedEntity );
300 
301         instancedEntity->setInUse(false);
302         instancedEntity->stopSharingTransform();
303 
304         //Put it back into the queue
305         mUnusedEntities.push_back( instancedEntity );
306     }
307     //-----------------------------------------------------------------------
getInstancedEntitiesInUse(InstancedEntityVec & outEntities,CustomParamsVec & outParams)308     void InstanceBatch::getInstancedEntitiesInUse( InstancedEntityVec &outEntities,
309                                                     CustomParamsVec &outParams )
310     {
311         InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
312         InstancedEntityVec::const_iterator end  = mInstancedEntities.end();
313 
314         while( itor != end )
315         {
316             if( (*itor)->isInUse() )
317             {
318                 outEntities.push_back( *itor );
319 
320                 for( unsigned char i=0; i<mCreator->getNumCustomParams(); ++i )
321                     outParams.push_back( _getCustomParam( *itor, i ) );
322             }
323 
324             ++itor;
325         }
326     }
327     //-----------------------------------------------------------------------
defragmentBatchNoCull(InstancedEntityVec & usedEntities,CustomParamsVec & usedParams)328     void InstanceBatch::defragmentBatchNoCull( InstancedEntityVec &usedEntities,
329                                                 CustomParamsVec &usedParams )
330     {
331         const size_t maxInstancesToCopy = std::min( mInstancesPerBatch, usedEntities.size() );
332         InstancedEntityVec::iterator first = usedEntities.end() - maxInstancesToCopy;
333         CustomParamsVec::iterator firstParams = usedParams.end() - maxInstancesToCopy *
334                                                                     mCreator->getNumCustomParams();
335 
336         //Copy from the back to front, into m_instancedEntities
337         mInstancedEntities.insert( mInstancedEntities.begin(), first, usedEntities.end() );
338         //Remove them from the array
339         usedEntities.resize( usedEntities.size() - maxInstancesToCopy );
340 
341         mCustomParams.insert( mCustomParams.begin(), firstParams, usedParams.end() );
342     }
343     //-----------------------------------------------------------------------
defragmentBatchDoCull(InstancedEntityVec & usedEntities,CustomParamsVec & usedParams)344     void InstanceBatch::defragmentBatchDoCull( InstancedEntityVec &usedEntities,
345                                                 CustomParamsVec &usedParams )
346     {
347         //Get the the entity closest to the minimum bbox edge and put into "first"
348         InstancedEntityVec::const_iterator itor   = usedEntities.begin();
349         InstancedEntityVec::const_iterator end   = usedEntities.end();
350 
351         Vector3 vMinPos = Vector3::ZERO, firstPos = Vector3::ZERO;
352         InstancedEntity *first = 0;
353 
354         if( !usedEntities.empty() )
355         {
356             first      = *usedEntities.begin();
357             firstPos   = first->_getDerivedPosition();
358             vMinPos      = first->_getDerivedPosition();
359         }
360 
361         while( itor != end )
362         {
363             const Vector3 &vPos      = (*itor)->_getDerivedPosition();
364 
365             vMinPos.x = std::min( vMinPos.x, vPos.x );
366             vMinPos.y = std::min( vMinPos.y, vPos.y );
367             vMinPos.z = std::min( vMinPos.z, vPos.z );
368 
369             if( vMinPos.squaredDistance( vPos ) < vMinPos.squaredDistance( firstPos ) )
370             {
371                 firstPos   = vPos;
372             }
373 
374             ++itor;
375         }
376 
377         //Now collect entities closest to 'first'
378         while( !usedEntities.empty() && mInstancedEntities.size() < mInstancesPerBatch )
379         {
380             InstancedEntityVec::iterator closest   = usedEntities.begin();
381             InstancedEntityVec::iterator it         = usedEntities.begin();
382             InstancedEntityVec::iterator e          = usedEntities.end();
383 
384             Vector3 closestPos;
385             closestPos = (*closest)->_getDerivedPosition();
386 
387             while( it != e )
388             {
389                 const Vector3 &vPos   = (*it)->_getDerivedPosition();
390 
391                 if( firstPos.squaredDistance( vPos ) < firstPos.squaredDistance( closestPos ) )
392                 {
393                     closest      = it;
394                     closestPos   = vPos;
395                 }
396 
397                 ++it;
398             }
399 
400             mInstancedEntities.push_back( *closest );
401             //Now the custom params
402             const size_t idx = closest - usedEntities.begin();
403             for( unsigned char i=0; i<mCreator->getNumCustomParams(); ++i )
404             {
405                 mCustomParams.push_back( usedParams[idx + i] );
406             }
407 
408             //Remove 'closest' from usedEntities & usedParams using swap and pop_back trick
409             *closest = *(usedEntities.end() - 1);
410             usedEntities.pop_back();
411 
412             for( unsigned char i=1; i<=mCreator->getNumCustomParams(); ++i )
413             {
414                 usedParams[idx + mCreator->getNumCustomParams() - i] = *(usedParams.end() - 1);
415                 usedParams.pop_back();
416             }
417         }
418     }
419     //-----------------------------------------------------------------------
_defragmentBatch(bool optimizeCulling,InstancedEntityVec & usedEntities,CustomParamsVec & usedParams)420     void InstanceBatch::_defragmentBatch( bool optimizeCulling, InstancedEntityVec &usedEntities,
421                                             CustomParamsVec &usedParams )
422     {
423         //Remove and clear what we don't need
424         mInstancedEntities.clear();
425         mCustomParams.clear();
426         deleteUnusedInstancedEntities();
427 
428         if( !optimizeCulling )
429             defragmentBatchNoCull( usedEntities, usedParams );
430         else
431             defragmentBatchDoCull( usedEntities, usedParams );
432 
433         //Reassign instance IDs and tell we're the new parent
434         uint32 instanceId = 0;
435         InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
436         InstancedEntityVec::const_iterator end  = mInstancedEntities.end();
437 
438         while( itor != end )
439         {
440             (*itor)->mInstanceId = instanceId++;
441             (*itor)->mBatchOwner = this;
442             ++itor;
443         }
444 
445         //Recreate unused entities, if there's left space in our container
446         assert( (signed)(mInstancesPerBatch) - (signed)(mInstancedEntities.size()) >= 0 );
447         mInstancedEntities.reserve( mInstancesPerBatch );
448         mUnusedEntities.reserve( mInstancesPerBatch );
449         mCustomParams.reserve( mCreator->getNumCustomParams() * mInstancesPerBatch );
450         for( size_t i=mInstancedEntities.size(); i<mInstancesPerBatch; ++i )
451         {
452             InstancedEntity *instance = generateInstancedEntity(i);
453             mInstancedEntities.push_back( instance );
454             mUnusedEntities.push_back( instance );
455             mCustomParams.push_back( Ogre::Vector4::ZERO );
456         }
457 
458         //We've potentially changed our bounds
459         if( !isBatchUnused() )
460             _boundsDirty();
461     }
462     //-----------------------------------------------------------------------
_defragmentBatchDiscard(void)463     void InstanceBatch::_defragmentBatchDiscard(void)
464     {
465         //Remove and clear what we don't need
466         mInstancedEntities.clear();
467         deleteUnusedInstancedEntities();
468     }
469     //-----------------------------------------------------------------------
_boundsDirty(void)470     void InstanceBatch::_boundsDirty(void)
471     {
472         if( mCreator && !mBoundsDirty )
473             mCreator->_addDirtyBatch( this );
474         mBoundsDirty = true;
475     }
476     //-----------------------------------------------------------------------
getMovableType(void) const477     const String& InstanceBatch::getMovableType(void) const
478     {
479         static String sType = "InstanceBatch";
480         return sType;
481     }
482     //-----------------------------------------------------------------------
_notifyCurrentCamera(Camera * cam)483     void InstanceBatch::_notifyCurrentCamera( Camera* cam )
484     {
485         mCurrentCamera = cam;
486 
487         //See DistanceLodStrategy::getValueImpl()
488         //We use our own because our SceneNode is just filled with zeroes, and updating it
489         //with real values is expensive, plus we would need to make sure it doesn't get to
490         //the shader
491 		Real depth = Math::Sqrt(getSquaredViewDepth(cam)) - getBoundingRadius();
492         depth = std::max( depth, Real(0) );
493 
494         Real lodValue = depth * cam->_getLodBiasInverse();
495 
496         //Now calculate Material LOD
497         /*const LodStrategy *materialStrategy = m_material->getLodStrategy();
498 
499         //Calculate LOD value for given strategy
500         Real lodValue = materialStrategy->getValue( this, cam );*/
501 
502         //Get the index at this depth
503         unsigned short idx = mMaterial->getLodIndex( lodValue );
504 
505         //TODO: Replace subEntity for MovableObject
506         // Construct event object
507         /*EntityMaterialLodChangedEvent subEntEvt;
508         subEntEvt.subEntity = this;
509         subEntEvt.camera = cam;
510         subEntEvt.lodValue = lodValue;
511         subEntEvt.previousLodIndex = m_materialLodIndex;
512         subEntEvt.newLodIndex = idx;
513 
514         //Notify LOD event listeners
515         cam->getSceneManager()->_notifyEntityMaterialLodChanged(subEntEvt);*/
516 
517         // Change LOD index
518         mMaterialLodIndex = idx;
519 
520         mBeyondFarDistance = false;
521 
522         if (cam->getUseRenderingDistance() && mUpperDistance > 0)
523         {
524             if (depth > mUpperDistance)
525                 mBeyondFarDistance = true;
526         }
527 
528         if (!mBeyondFarDistance && cam->getUseMinPixelSize() && mMinPixelSize > 0)
529         {
530             Real pixelRatio = cam->getPixelDisplayRatio();
531 
532             Ogre::Vector3 objBound =
533                 getBoundingBox().getSize() * getParentNode()->_getDerivedScale();
534             objBound.x = Math::Sqr(objBound.x);
535             objBound.y = Math::Sqr(objBound.y);
536             objBound.z = Math::Sqr(objBound.z);
537             float sqrObjMedianSize = std::max(
538                 std::max(std::min(objBound.x, objBound.y), std::min(objBound.x, objBound.z)),
539                 std::min(objBound.y, objBound.z));
540 
541             // If we have a perspective camera calculations are done relative to distance
542             Real sqrDistance = 1;
543 
544             if (cam->getProjectionType() == PT_PERSPECTIVE)
545                 sqrDistance = getSquaredViewDepth(cam->getLodCamera()); // it's ok
546 
547             mBeyondFarDistance =
548                 sqrObjMedianSize < sqrDistance * Math::Sqr(pixelRatio * mMinPixelSize);
549         }
550 
551         if (mParentNode)
552         {
553             MovableObjectLodChangedEvent evt;
554             evt.movableObject = this;
555             evt.camera = cam;
556 
557             cam->getSceneManager()->_notifyMovableObjectLodChanged(evt);
558         }
559 
560         mRenderingDisabled = mListener && !mListener->objectRendering(this, cam);
561 
562         // MovableObject::_notifyCurrentCamera( cam ); // it does not suit
563     }
564     //-----------------------------------------------------------------------
getBoundingBox(void) const565     const AxisAlignedBox& InstanceBatch::getBoundingBox(void) const
566     {
567         return mFullBoundingBox;
568     }
569     //-----------------------------------------------------------------------
getBoundingRadius(void) const570     Real InstanceBatch::getBoundingRadius(void) const
571     {
572         return mBoundingRadius;
573     }
574     //-----------------------------------------------------------------------
getSquaredViewDepth(const Camera * cam) const575     Real InstanceBatch::getSquaredViewDepth( const Camera* cam ) const
576     {
577         unsigned long currentFrameNumber = Root::getSingleton().getNextFrameNumber();
578 
579         if (mCameraDistLastUpdateFrameNumber != currentFrameNumber || mCachedCamera != cam)
580         {
581             mCachedCameraDist =
582                 getBoundingBox().getCenter().squaredDistance(cam->getDerivedPosition());
583 
584             mCachedCamera = cam;
585             mCameraDistLastUpdateFrameNumber = currentFrameNumber;
586         }
587 
588         return mCachedCameraDist;
589     }
590     //-----------------------------------------------------------------------
getLights(void) const591     const LightList& InstanceBatch::getLights( void ) const
592     {
593         return queryLights();
594     }
595     //-----------------------------------------------------------------------
getTechnique(void) const596     Technique* InstanceBatch::getTechnique( void ) const
597     {
598         return mMaterial->getBestTechnique( mMaterialLodIndex, this );
599     }
600     //-----------------------------------------------------------------------
_updateRenderQueue(RenderQueue * queue)601     void InstanceBatch::_updateRenderQueue( RenderQueue* queue )
602     {
603         /*if( m_boundsDirty )
604             _updateBounds();*/
605 
606         mDirtyAnimation = false;
607 
608         //Is at least one object in the scene?
609         updateVisibility();
610 
611         if( mVisible )
612         {
613             if( mMeshReference->hasSkeleton() )
614             {
615                 InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
616                 InstancedEntityVec::const_iterator end  = mInstancedEntities.end();
617 
618                 while( itor != end )
619                 {
620                     mDirtyAnimation |= (*itor)->_updateAnimation();
621                     ++itor;
622                 }
623             }
624 
625             queue->addRenderable( this, mRenderQueueID, mRenderQueuePriority );
626         }
627 
628         //Reset visibility once we skipped addRenderable (which saves GPU time), because OGRE for some
629         //reason stops updating our render queue afterwards, preventing us to recalculate visibility
630         mVisible = true;
631     }
632     //-----------------------------------------------------------------------
visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)633     void InstanceBatch::visitRenderables( Renderable::Visitor* visitor, bool debugRenderables )
634     {
635         visitor->visit( this, 0, false );
636     }
637     //-----------------------------------------------------------------------
_setCustomParam(InstancedEntity * instancedEntity,unsigned char idx,const Vector4 & newParam)638     void InstanceBatch::_setCustomParam( InstancedEntity *instancedEntity, unsigned char idx,
639                                          const Vector4 &newParam )
640     {
641         mCustomParams[instancedEntity->mInstanceId * mCreator->getNumCustomParams() + idx] = newParam;
642     }
643     //-----------------------------------------------------------------------
_getCustomParam(InstancedEntity * instancedEntity,unsigned char idx)644     const Vector4& InstanceBatch::_getCustomParam( InstancedEntity *instancedEntity, unsigned char idx )
645     {
646         return mCustomParams[instancedEntity->mInstanceId * mCreator->getNumCustomParams() + idx];
647     }
648 }
649