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