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-2013 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 "OgreInstancedGeometry.h" 30 #include "OgreEntity.h" 31 #include "OgreSubEntity.h" 32 #include "OgreSceneNode.h" 33 #include "OgreException.h" 34 #include "OgreMesh.h" 35 #include "OgreSubMesh.h" 36 #include "OgreLogManager.h" 37 #include "OgreSceneManager.h" 38 #include "OgreCamera.h" 39 #include "OgreMaterialManager.h" 40 #include "OgreRoot.h" 41 #include "OgreRenderSystem.h" 42 #include "OgreEdgeListBuilder.h" 43 #include "OgreStringConverter.h" 44 45 namespace Ogre { 46 47 #define BatchInstance_RANGE 1024 48 #define BatchInstance_HALF_RANGE 512 49 #define BatchInstance_MAX_INDEX 511 50 #define BatchInstance_MIN_INDEX -512 51 52 //-------------------------------------------------------------------------- InstancedGeometry(SceneManager * owner,const String & name)53 InstancedGeometry::InstancedGeometry(SceneManager* owner, const String& name): 54 mOwner(owner), 55 mName(name), 56 mBuilt(false), 57 mUpperDistance(0.0f), 58 mSquaredUpperDistance(0.0f), 59 mCastShadows(false), 60 mBatchInstanceDimensions(Vector3(1000,1000,1000)), 61 mHalfBatchInstanceDimensions(Vector3(500,500,500)), 62 mOrigin(Vector3(0,0,0)), 63 mVisible(true), 64 mProvideWorldInverses(false), 65 mRenderQueueID(RENDER_QUEUE_MAIN), 66 mRenderQueueIDSet(false), 67 mObjectCount(0), 68 mInstancedGeometryInstance(0), 69 mSkeletonInstance(0), 70 mAnimationState(0) 71 { 72 mBaseSkeleton.setNull(); 73 } 74 //-------------------------------------------------------------------------- ~InstancedGeometry()75 InstancedGeometry::~InstancedGeometry() 76 { 77 reset(); 78 if(mSkeletonInstance) 79 OGRE_DELETE mSkeletonInstance; 80 81 82 } 83 //-------------------------------------------------------------------------- getInstancedGeometryInstance(void)84 InstancedGeometry::BatchInstance*InstancedGeometry::getInstancedGeometryInstance(void) 85 { 86 if (!mInstancedGeometryInstance) 87 { 88 uint32 index = 0; 89 // Make a name 90 StringUtil::StrStreamType str; 91 str << mName << ":" << index; 92 93 mInstancedGeometryInstance = OGRE_NEW BatchInstance(this, str.str(), mOwner, index); 94 mOwner->injectMovableObject(mInstancedGeometryInstance); 95 mInstancedGeometryInstance->setVisible(mVisible); 96 mInstancedGeometryInstance->setCastShadows(mCastShadows); 97 if (mRenderQueueIDSet) 98 { 99 mInstancedGeometryInstance->setRenderQueueGroup(mRenderQueueID); 100 } 101 mBatchInstanceMap[index] = mInstancedGeometryInstance; 102 103 104 } 105 return mInstancedGeometryInstance; 106 } 107 //-------------------------------------------------------------------------- getBatchInstance(const AxisAlignedBox & bounds,bool autoCreate)108 InstancedGeometry::BatchInstance* InstancedGeometry::getBatchInstance(const AxisAlignedBox& bounds, 109 bool autoCreate) 110 { 111 if (bounds.isNull()) 112 return 0; 113 114 // Get the BatchInstance which has the largest overlapping volume 115 const Vector3 min = bounds.getMinimum(); 116 const Vector3 max = bounds.getMaximum(); 117 118 // Get the min and max BatchInstance indexes 119 ushort minx, miny, minz; 120 ushort maxx, maxy, maxz; 121 getBatchInstanceIndexes(min, minx, miny, minz); 122 getBatchInstanceIndexes(max, maxx, maxy, maxz); 123 Real maxVolume = 0.0f; 124 ushort finalx =0 , finaly = 0, finalz = 0; 125 for (ushort x = minx; x <= maxx; ++x) 126 { 127 for (ushort y = miny; y <= maxy; ++y) 128 { 129 for (ushort z = minz; z <= maxz; ++z) 130 { 131 Real vol = getVolumeIntersection(bounds, x, y, z); 132 if (vol > maxVolume) 133 { 134 maxVolume = vol; 135 finalx = x; 136 finaly = y; 137 finalz = z; 138 } 139 140 } 141 } 142 } 143 144 assert(maxVolume > 0.0f && 145 "Static geometry: Problem determining closest volume match!"); 146 147 return getBatchInstance(finalx, finaly, finalz, autoCreate); 148 149 } 150 //-------------------------------------------------------------------------- getVolumeIntersection(const AxisAlignedBox & box,ushort x,ushort y,ushort z)151 Real InstancedGeometry::getVolumeIntersection(const AxisAlignedBox& box, 152 ushort x, ushort y, ushort z) 153 { 154 // Get bounds of indexed BatchInstance 155 AxisAlignedBox BatchInstanceBounds = getBatchInstanceBounds(x, y, z); 156 AxisAlignedBox intersectBox = BatchInstanceBounds.intersection(box); 157 // return a 'volume' which ignores zero dimensions 158 // since we only use this for relative comparisons of the same bounds 159 // this will still be internally consistent 160 Vector3 boxdiff = box.getMaximum() - box.getMinimum(); 161 Vector3 intersectDiff = intersectBox.getMaximum() - intersectBox.getMinimum(); 162 163 return (boxdiff.x == 0 ? 1 : intersectDiff.x) * 164 (boxdiff.y == 0 ? 1 : intersectDiff.y) * 165 (boxdiff.z == 0 ? 1 : intersectDiff.z); 166 167 } 168 //-------------------------------------------------------------------------- getBatchInstanceBounds(ushort x,ushort y,ushort z)169 AxisAlignedBox InstancedGeometry::getBatchInstanceBounds(ushort x, ushort y, ushort z) 170 { 171 Vector3 min( 172 ((Real)x - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.x + mOrigin.x, 173 ((Real)y - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.y + mOrigin.y, 174 ((Real)z - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.z + mOrigin.z 175 ); 176 Vector3 max = min + mBatchInstanceDimensions; 177 return AxisAlignedBox(min, max); 178 } 179 //-------------------------------------------------------------------------- getBatchInstanceCentre(ushort x,ushort y,ushort z)180 Vector3 InstancedGeometry::getBatchInstanceCentre(ushort x, ushort y, ushort z) 181 { 182 return Vector3( 183 ((Real)x - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.x + mOrigin.x 184 + mHalfBatchInstanceDimensions.x, 185 ((Real)y - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.y + mOrigin.y 186 + mHalfBatchInstanceDimensions.y, 187 ((Real)z - BatchInstance_HALF_RANGE) * mBatchInstanceDimensions.z + mOrigin.z 188 + mHalfBatchInstanceDimensions.z 189 ); 190 } 191 //-------------------------------------------------------------------------- getBatchInstance(ushort x,ushort y,ushort z,bool autoCreate)192 InstancedGeometry::BatchInstance* InstancedGeometry::getBatchInstance( 193 ushort x, ushort y, ushort z, bool autoCreate) 194 { 195 uint32 index = packIndex(x, y, z); 196 BatchInstance* ret = getBatchInstance(index); 197 if (!ret && autoCreate) 198 { 199 // Make a name 200 StringUtil::StrStreamType str; 201 str << mName << ":" << index; 202 // Calculate the BatchInstance centre 203 Vector3 centre(0,0,0);// = getBatchInstanceCentre(x, y, z); 204 ret = OGRE_NEW BatchInstance(this, str.str(), mOwner, index/*, centre*/); 205 mOwner->injectMovableObject(ret); 206 ret->setVisible(mVisible); 207 ret->setCastShadows(mCastShadows); 208 if (mRenderQueueIDSet) 209 { 210 ret->setRenderQueueGroup(mRenderQueueID); 211 } 212 mBatchInstanceMap[index] = ret; 213 } 214 return ret; 215 } 216 //-------------------------------------------------------------------------- getBatchInstance(uint32 index)217 InstancedGeometry::BatchInstance* InstancedGeometry::getBatchInstance(uint32 index) 218 { 219 BatchInstanceMap::iterator i = mBatchInstanceMap.find(index); 220 if (i != mBatchInstanceMap.end()) 221 { 222 return i->second; 223 } 224 else 225 { 226 return 0; 227 } 228 229 } 230 //-------------------------------------------------------------------------- getBatchInstanceIndexes(const Vector3 & point,ushort & x,ushort & y,ushort & z)231 void InstancedGeometry::getBatchInstanceIndexes(const Vector3& point, 232 ushort& x, ushort& y, ushort& z) 233 { 234 // Scale the point into multiples of BatchInstance and adjust for origin 235 Vector3 scaledPoint = (point - mOrigin) / mBatchInstanceDimensions; 236 237 // Round down to 'bottom left' point which represents the cell index 238 int ix = Math::IFloor(scaledPoint.x); 239 int iy = Math::IFloor(scaledPoint.y); 240 int iz = Math::IFloor(scaledPoint.z); 241 242 // Check bounds 243 if (ix < BatchInstance_MIN_INDEX || ix > BatchInstance_MAX_INDEX 244 || iy < BatchInstance_MIN_INDEX || iy > BatchInstance_MAX_INDEX 245 || iz < BatchInstance_MIN_INDEX || iz > BatchInstance_MAX_INDEX) 246 { 247 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 248 "Point out of bounds", 249 "InstancedGeometry::getBatchInstanceIndexes"); 250 } 251 // Adjust for the fact that we use unsigned values for simplicity 252 // (requires less faffing about for negatives give 10-bit packing 253 x = static_cast<ushort>(ix + BatchInstance_HALF_RANGE); 254 y = static_cast<ushort>(iy + BatchInstance_HALF_RANGE); 255 z = static_cast<ushort>(iz + BatchInstance_HALF_RANGE); 256 257 258 } 259 //-------------------------------------------------------------------------- packIndex(ushort x,ushort y,ushort z)260 uint32 InstancedGeometry::packIndex(ushort x, ushort y, ushort z) 261 { 262 return x + (y << 10) + (z << 20); 263 } 264 //-------------------------------------------------------------------------- getBatchInstance(const Vector3 & point,bool autoCreate)265 InstancedGeometry::BatchInstance* InstancedGeometry::getBatchInstance(const Vector3& point, 266 bool autoCreate) 267 { 268 ushort x, y, z; 269 getBatchInstanceIndexes(point, x, y, z); 270 return getBatchInstance(x, y, z, autoCreate); 271 } 272 //-------------------------------------------------------------------------- calculateBounds(VertexData * vertexData,const Vector3 & position,const Quaternion & orientation,const Vector3 & scale)273 AxisAlignedBox InstancedGeometry::calculateBounds(VertexData* vertexData, 274 const Vector3& position, const Quaternion& orientation, 275 const Vector3& scale) 276 { 277 const VertexElement* posElem = 278 vertexData->vertexDeclaration->findElementBySemantic( 279 VES_POSITION); 280 HardwareVertexBufferSharedPtr vbuf = 281 vertexData->vertexBufferBinding->getBuffer(posElem->getSource()); 282 unsigned char* vertex = 283 static_cast<unsigned char*>( 284 vbuf->lock(HardwareBuffer::HBL_READ_ONLY)); 285 float* pFloat; 286 287 Vector3 min = Vector3::ZERO, max = Vector3::UNIT_SCALE; 288 bool first = true; 289 290 for(size_t j = 0; j < vertexData->vertexCount; ++j, vertex += vbuf->getVertexSize()) 291 { 292 posElem->baseVertexPointerToElement(vertex, &pFloat); 293 294 Vector3 pt; 295 296 pt.x = (*pFloat++); 297 pt.y = (*pFloat++); 298 pt.z = (*pFloat++); 299 // Transform to world (scale, rotate, translate) 300 pt = (orientation * (pt * scale)) + position; 301 if (first) 302 { 303 min = max = pt; 304 first = false; 305 } 306 else 307 { 308 min.makeFloor(pt); 309 max.makeCeil(pt); 310 } 311 312 } 313 vbuf->unlock(); 314 return AxisAlignedBox(min, max); 315 } 316 //-------------------------------------------------------------------------- addEntity(Entity * ent,const Vector3 & position,const Quaternion & orientation,const Vector3 & scale)317 void InstancedGeometry::addEntity(Entity* ent, const Vector3& position, 318 const Quaternion& orientation, const Vector3& scale) 319 { 320 321 const MeshPtr& msh = ent->getMesh(); 322 // Validate 323 if (msh->isLodManual()) 324 { 325 LogManager::getSingleton().logMessage( 326 "WARNING (InstancedGeometry): Manual LOD is not supported. " 327 "Using only highest LOD level for mesh " + msh->getName(), LML_CRITICAL); 328 } 329 330 //get the skeleton of the entity, if that's not already done 331 if(!ent->getMesh()->getSkeleton().isNull()&&mBaseSkeleton.isNull()) 332 { 333 mBaseSkeleton=ent->getMesh()->getSkeleton(); 334 mSkeletonInstance= OGRE_NEW SkeletonInstance(mBaseSkeleton); 335 mSkeletonInstance->load(); 336 mAnimationState=ent->getAllAnimationStates(); 337 } 338 AxisAlignedBox sharedWorldBounds; 339 // queue this entities submeshes and choice of material 340 // also build the lists of geometry to be used for the source of lods 341 342 343 for (uint i = 0; i < ent->getNumSubEntities(); ++i) 344 { 345 SubEntity* se = ent->getSubEntity(i); 346 QueuedSubMesh* q = OGRE_NEW QueuedSubMesh(); 347 348 // Get the geometry for this SubMesh 349 q->submesh = se->getSubMesh(); 350 q->geometryLodList = determineGeometry(q->submesh); 351 q->materialName = se->getMaterialName(); 352 q->orientation = orientation; 353 q->position = position; 354 q->scale = scale; 355 q->ID = mObjectCount; 356 // Determine the bounds based on the highest LOD 357 q->worldBounds = calculateBounds( 358 (*q->geometryLodList)[0].vertexData, 359 position, orientation, scale); 360 361 mQueuedSubMeshes.push_back(q); 362 } 363 mObjectCount++; 364 365 } 366 //-------------------------------------------------------------------------- 367 InstancedGeometry::SubMeshLodGeometryLinkList* determineGeometry(SubMesh * sm)368 InstancedGeometry::determineGeometry(SubMesh* sm) 369 { 370 // First, determine if we've already seen this submesh before 371 SubMeshGeometryLookup::iterator i = 372 mSubMeshGeometryLookup.find(sm); 373 if (i != mSubMeshGeometryLookup.end()) 374 { 375 return i->second; 376 } 377 // Otherwise, we have to create a new one 378 SubMeshLodGeometryLinkList* lodList = OGRE_NEW_T(SubMeshLodGeometryLinkList, MEMCATEGORY_GEOMETRY)(); 379 mSubMeshGeometryLookup[sm] = lodList; 380 ushort numLods = sm->parent->isLodManual() ? 1 : 381 sm->parent->getNumLodLevels(); 382 lodList->resize(numLods); 383 for (ushort lod = 0; lod < numLods; ++lod) 384 { 385 SubMeshLodGeometryLink& geomLink = (*lodList)[lod]; 386 IndexData *lodIndexData; 387 if (lod == 0) 388 { 389 lodIndexData = sm->indexData; 390 } 391 else 392 { 393 lodIndexData = sm->mLodFaceList[lod - 1]; 394 } 395 // Can use the original mesh geometry? 396 if (sm->useSharedVertices) 397 { 398 if (sm->parent->getNumSubMeshes() == 1) 399 { 400 // Ok, this is actually our own anyway 401 geomLink.vertexData = sm->parent->sharedVertexData; 402 geomLink.indexData = lodIndexData; 403 } 404 else 405 { 406 // We have to split it 407 splitGeometry(sm->parent->sharedVertexData, 408 lodIndexData, &geomLink); 409 } 410 } 411 else 412 { 413 if (lod == 0) 414 { 415 // Ok, we can use the existing geometry; should be in full 416 // use by just this SubMesh 417 geomLink.vertexData = sm->vertexData; 418 geomLink.indexData = sm->indexData; 419 } 420 else 421 { 422 // We have to split it 423 splitGeometry(sm->vertexData, 424 lodIndexData, &geomLink); 425 } 426 } 427 assert (geomLink.vertexData->vertexStart == 0 && 428 "Cannot use vertexStart > 0 on indexed geometry due to " 429 "rendersystem incompatibilities - see the docs!"); 430 } 431 432 433 return lodList; 434 } 435 //-------------------------------------------------------------------------- splitGeometry(VertexData * vd,IndexData * id,InstancedGeometry::SubMeshLodGeometryLink * targetGeomLink)436 void InstancedGeometry::splitGeometry(VertexData* vd, IndexData* id, 437 InstancedGeometry::SubMeshLodGeometryLink* targetGeomLink) 438 { 439 // Firstly we need to scan to see how many vertices are being used 440 // and while we're at it, build the remap we can use later 441 bool use32bitIndexes = 442 id->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT; 443 IndexRemap indexRemap; 444 if (use32bitIndexes) 445 { 446 uint32 *p32 = static_cast<uint32*>(id->indexBuffer->lock( 447 id->indexStart, 448 id->indexCount * id->indexBuffer->getIndexSize(), 449 HardwareBuffer::HBL_READ_ONLY)); 450 buildIndexRemap(p32, id->indexCount, indexRemap); 451 id->indexBuffer->unlock(); 452 } 453 else 454 { 455 uint16 *p16 = static_cast<uint16*>(id->indexBuffer->lock( 456 id->indexStart, 457 id->indexCount * id->indexBuffer->getIndexSize(), 458 HardwareBuffer::HBL_READ_ONLY)); 459 buildIndexRemap(p16, id->indexCount, indexRemap); 460 id->indexBuffer->unlock(); 461 } 462 if (indexRemap.size() == vd->vertexCount) 463 { 464 // ha, complete usage after all 465 targetGeomLink->vertexData = vd; 466 targetGeomLink->indexData = id; 467 return; 468 } 469 470 471 // Create the new vertex data records 472 targetGeomLink->vertexData = vd->clone(false); 473 // Convenience 474 VertexData* newvd = targetGeomLink->vertexData; 475 //IndexData* newid = targetGeomLink->indexData; 476 // Update the vertex count 477 newvd->vertexCount = indexRemap.size(); 478 479 size_t numvbufs = vd->vertexBufferBinding->getBufferCount(); 480 // Copy buffers from old to new 481 for (unsigned short b = 0; b < numvbufs; ++b) 482 { 483 // Lock old buffer 484 HardwareVertexBufferSharedPtr oldBuf = 485 vd->vertexBufferBinding->getBuffer(b); 486 // Create new buffer 487 HardwareVertexBufferSharedPtr newBuf = 488 HardwareBufferManager::getSingleton().createVertexBuffer( 489 oldBuf->getVertexSize(), 490 indexRemap.size(), 491 HardwareBuffer::HBU_STATIC); 492 // rebind 493 newvd->vertexBufferBinding->setBinding(b, newBuf); 494 495 // Copy all the elements of the buffer across, by iterating over 496 // the IndexRemap which describes how to move the old vertices 497 // to the new ones. By nature of the map the remap is in order of 498 // indexes in the old buffer, but note that we're not guaranteed to 499 // address every vertex (which is kinda why we're here) 500 uchar* pSrcBase = static_cast<uchar*>( 501 oldBuf->lock(HardwareBuffer::HBL_READ_ONLY)); 502 uchar* pDstBase = static_cast<uchar*>( 503 newBuf->lock(HardwareBuffer::HBL_DISCARD)); 504 size_t vertexSize = oldBuf->getVertexSize(); 505 // Buffers should be the same size 506 assert (vertexSize == newBuf->getVertexSize()); 507 508 for (IndexRemap::iterator r = indexRemap.begin(); 509 r != indexRemap.end(); ++r) 510 { 511 assert (r->first < oldBuf->getNumVertices()); 512 assert (r->second < newBuf->getNumVertices()); 513 514 uchar* pSrc = pSrcBase + r->first * vertexSize; 515 uchar* pDst = pDstBase + r->second * vertexSize; 516 memcpy(pDst, pSrc, vertexSize); 517 } 518 // unlock 519 oldBuf->unlock(); 520 newBuf->unlock(); 521 522 } 523 524 // Now create a new index buffer 525 HardwareIndexBufferSharedPtr ibuf = 526 HardwareBufferManager::getSingleton().createIndexBuffer( 527 id->indexBuffer->getType(), id->indexCount, 528 HardwareBuffer::HBU_STATIC); 529 530 if (use32bitIndexes) 531 { 532 uint32 *pSrc32, *pDst32; 533 pSrc32 = static_cast<uint32*>(id->indexBuffer->lock( 534 id->indexStart, id->indexCount * id->indexBuffer->getIndexSize(), 535 HardwareBuffer::HBL_READ_ONLY)); 536 pDst32 = static_cast<uint32*>(ibuf->lock( 537 HardwareBuffer::HBL_DISCARD)); 538 remapIndexes(pSrc32, pDst32, indexRemap, id->indexCount); 539 id->indexBuffer->unlock(); 540 ibuf->unlock(); 541 } 542 else 543 { 544 uint16 *pSrc16, *pDst16; 545 pSrc16 = static_cast<uint16*>(id->indexBuffer->lock( 546 id->indexStart, id->indexCount * id->indexBuffer->getIndexSize(), 547 HardwareBuffer::HBL_READ_ONLY)); 548 pDst16 = static_cast<uint16*>(ibuf->lock( 549 HardwareBuffer::HBL_DISCARD)); 550 remapIndexes(pSrc16, pDst16, indexRemap, id->indexCount); 551 id->indexBuffer->unlock(); 552 ibuf->unlock(); 553 } 554 555 targetGeomLink->indexData = OGRE_NEW IndexData(); 556 targetGeomLink->indexData->indexStart = 0; 557 targetGeomLink->indexData->indexCount = id->indexCount; 558 targetGeomLink->indexData->indexBuffer = ibuf; 559 560 // Store optimised geometry for deallocation later 561 OptimisedSubMeshGeometry *optGeom = OGRE_NEW OptimisedSubMeshGeometry(); 562 optGeom->indexData = targetGeomLink->indexData; 563 optGeom->vertexData = targetGeomLink->vertexData; 564 mOptimisedSubMeshGeometryList.push_back(optGeom); 565 } 566 //-------------------------------------------------------------------------- addSceneNode(const SceneNode * node)567 void InstancedGeometry::addSceneNode(const SceneNode* node) 568 { 569 SceneNode::ConstObjectIterator obji = node->getAttachedObjectIterator(); 570 while (obji.hasMoreElements()) 571 { 572 MovableObject* mobj = obji.getNext(); 573 if (mobj->getMovableType() == "Entity") 574 { 575 addEntity(static_cast<Entity*>(mobj), 576 node->_getDerivedPosition(), 577 node->_getDerivedOrientation(), 578 node->_getDerivedScale()); 579 } 580 } 581 // Iterate through all the child-nodes 582 SceneNode::ConstChildNodeIterator nodei = node->getChildIterator(); 583 584 while (nodei.hasMoreElements()) 585 { 586 const SceneNode* newNode = static_cast<const SceneNode*>(nodei.getNext()); 587 // Add this subnode and its children... 588 addSceneNode( newNode ); 589 } 590 } 591 //-------------------------------------------------------------------------- build(void)592 void InstancedGeometry::build(void) 593 { 594 // Make sure there's nothing from previous builds 595 destroy(); 596 597 // Firstly allocate meshes to BatchInstances 598 for (QueuedSubMeshList::iterator qi = mQueuedSubMeshes.begin(); 599 qi != mQueuedSubMeshes.end(); ++qi) 600 { 601 QueuedSubMesh* qsm = *qi; 602 //BatchInstance* BatchInstance = getBatchInstance(qsm->worldBounds, true); 603 BatchInstance* batchInstance = getInstancedGeometryInstance(); 604 batchInstance->assign(qsm); 605 } 606 607 // Now tell each BatchInstance to build itself 608 for (BatchInstanceMap::iterator ri = mBatchInstanceMap.begin(); 609 ri != mBatchInstanceMap.end(); ++ri) 610 { 611 ri->second->build(); 612 } 613 614 615 } 616 //-------------------------------------------------------------------------- addBatchInstance(void)617 void InstancedGeometry::addBatchInstance(void) 618 { 619 620 621 BatchInstanceIterator regIt = getBatchInstanceIterator(); 622 BatchInstance* lastBatchInstance=0 ; 623 while(regIt.hasMoreElements()) 624 { 625 lastBatchInstance= regIt.getNext(); 626 } 627 628 if(!lastBatchInstance) 629 OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "No batch instance found", 630 "InstancedGeometry::addBatchInstance"); 631 632 uint32 index=(lastBatchInstance)?lastBatchInstance->getID()+1:0; 633 //create a new BatchInstance 634 635 BatchInstance*ret = OGRE_NEW BatchInstance(this, mName+":"+StringConverter::toString(index), 636 mOwner, index); 637 638 ret->attachToScene(); 639 640 mOwner->injectMovableObject(ret); 641 ret->setVisible(mVisible); 642 ret->setCastShadows(mCastShadows); 643 mBatchInstanceMap[index] = ret; 644 645 if (mRenderQueueIDSet) 646 { 647 ret->setRenderQueueGroup(mRenderQueueID); 648 } 649 650 const size_t numLod = lastBatchInstance->mLodValues.size(); 651 ret->mLodValues.resize(numLod); 652 for (ushort lod = 0; lod < numLod; lod++) 653 { 654 ret->mLodValues[lod] = 655 lastBatchInstance->mLodValues[lod]; 656 } 657 658 659 660 // update bounds 661 AxisAlignedBox box(lastBatchInstance->mAABB.getMinimum(),lastBatchInstance->mAABB.getMaximum()); 662 ret->mAABB.merge(box); 663 664 ret->mBoundingRadius = lastBatchInstance->mBoundingRadius ; 665 //now create news instanced objects 666 BatchInstance::ObjectsMap::iterator objIt; 667 for(objIt=lastBatchInstance->getInstancesMap().begin();objIt!=lastBatchInstance->getInstancesMap().end();++objIt) 668 { 669 InstancedObject* instancedObject = ret->isInstancedObjectPresent(objIt->first); 670 if(instancedObject == NULL) 671 { 672 if(mBaseSkeleton.isNull()) 673 { 674 instancedObject= OGRE_NEW InstancedObject(objIt->first); 675 } 676 else 677 { 678 instancedObject= OGRE_NEW InstancedObject(objIt->first,mSkeletonInstance,mAnimationState); 679 } 680 ret->addInstancedObject(objIt->first,instancedObject); 681 } 682 683 } 684 685 686 687 BatchInstance::LODIterator lodIterator = lastBatchInstance->getLODIterator(); 688 //parse all the LOD buckets of the BatchInstance 689 while (lodIterator.hasMoreElements()) 690 { 691 692 LODBucket* lod = lodIterator.getNext(); 693 //create a new LOD bucket for the new BatchInstance 694 LODBucket* lodBucket= OGRE_NEW LODBucket(ret, lod->getLod(), lod->getLodValue()); 695 696 //add the LODBucket to the BatchInstance list 697 ret->updateContainers(lodBucket); 698 699 LODBucket::MaterialIterator matIt = lod->getMaterialIterator(); 700 //parse all the material buckets of the LOD bucket 701 while (matIt.hasMoreElements()) 702 { 703 704 MaterialBucket*mat = matIt.getNext(); 705 //create a new material bucket 706 String materialName=mat->getMaterialName(); 707 MaterialBucket* matBucket = OGRE_NEW MaterialBucket(lodBucket,materialName); 708 709 //add the material bucket to the LOD buckets list and map 710 lodBucket->updateContainers(matBucket, materialName); 711 712 MaterialBucket::GeometryIterator geomIt = mat->getGeometryIterator(); 713 //parse all the geometry buckets of the material bucket 714 while(geomIt.hasMoreElements()) 715 { 716 //get the source geometry bucket 717 GeometryBucket *geom = geomIt.getNext(); 718 //create a new geometry bucket 719 GeometryBucket *geomBucket = OGRE_NEW GeometryBucket(matBucket,geom->getFormatString(),geom); 720 721 //update the material bucket map of the material bucket 722 matBucket->updateContainers(geomBucket, geomBucket->getFormatString() ); 723 724 //copy bounding information 725 geomBucket->getAABB()=geom->getAABB(); 726 geomBucket->setBoundingBox( geom->getBoundingBox()); 727 //now setups the news InstancedObjects. 728 for(objIt=ret->getInstancesMap().begin();objIt!=ret->getInstancesMap().end();++objIt) 729 { 730 //get the destination IntanciedObject 731 InstancedObject*obj=objIt->second; 732 InstancedObject::GeometryBucketList::iterator findIt; 733 //check if the bucket is not already in the list 734 findIt=std::find(obj->getGeometryBucketList().begin(),obj->getGeometryBucketList().end(),geomBucket); 735 if(findIt==obj->getGeometryBucketList().end()) 736 obj->addBucketToList(geomBucket); 737 } 738 739 740 } 741 } 742 } 743 } 744 //-------------------------------------------------------------------------- updateContainers(LODBucket * bucket)745 void InstancedGeometry::BatchInstance::updateContainers(LODBucket* bucket ) 746 { 747 mLodBucketList.push_back(bucket); 748 } 749 //-------------------------------------------------------------------------- updateContainers(MaterialBucket * bucket,String & name)750 void InstancedGeometry::LODBucket::updateContainers(MaterialBucket* bucket, String& name ) 751 { 752 mMaterialBucketMap[name] = bucket; 753 } 754 755 //-------------------------------------------------------------------------- getFormatString(void) const756 String InstancedGeometry::GeometryBucket::getFormatString(void) const 757 { 758 return mFormatString; 759 } 760 //-------------------------------------------------------------------------- attachToScene()761 void InstancedGeometry::BatchInstance::attachToScene() 762 { 763 764 mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(mName/*,mCentre*/); 765 mNode->attachObject(this); 766 } 767 //-------------------------------------------------------------------------- updateContainers(InstancedGeometry::GeometryBucket * bucket,const String & format)768 void InstancedGeometry::MaterialBucket::updateContainers(InstancedGeometry::GeometryBucket* bucket, const String & format) 769 { 770 mCurrentGeometryMap[format]=bucket; 771 mGeometryBucketList.push_back(bucket); 772 } setMaterial(const String & name)773 void InstancedGeometry::MaterialBucket:: setMaterial(const String & name) 774 { 775 mMaterial=MaterialManager::getSingleton().getByName(name); 776 } 777 //-------------------------------------------------------------------------- destroy(void)778 void InstancedGeometry::destroy(void) 779 { 780 RenderOperationVector::iterator it; 781 for(it=mRenderOps.begin();it!=mRenderOps.end();++it) 782 { 783 OGRE_DELETE (*it)->vertexData; 784 OGRE_DELETE (*it)->indexData; 785 786 } 787 mRenderOps.clear(); 788 789 // delete the BatchInstances 790 for (BatchInstanceMap::iterator i = mBatchInstanceMap.begin(); 791 i != mBatchInstanceMap.end(); ++i) 792 { 793 mOwner->extractMovableObject(i->second); 794 OGRE_DELETE i->second; 795 796 } 797 mBatchInstanceMap.clear(); 798 mInstancedGeometryInstance = NULL; 799 } 800 //-------------------------------------------------------------------------- reset(void)801 void InstancedGeometry::reset(void) 802 { 803 destroy(); 804 805 for (QueuedSubMeshList::iterator i = mQueuedSubMeshes.begin(); 806 i != mQueuedSubMeshes.end(); ++i) 807 { 808 OGRE_DELETE *i; 809 810 } 811 mQueuedSubMeshes.clear(); 812 // Delete precached geoemtry lists 813 for (SubMeshGeometryLookup::iterator l = mSubMeshGeometryLookup.begin(); 814 l != mSubMeshGeometryLookup.end(); ++l) 815 { 816 OGRE_DELETE_T(l->second, SubMeshLodGeometryLinkList, MEMCATEGORY_GEOMETRY); 817 818 } 819 mSubMeshGeometryLookup.clear(); 820 // Delete optimised geometry 821 for (OptimisedSubMeshGeometryList::iterator o = mOptimisedSubMeshGeometryList.begin(); 822 o != mOptimisedSubMeshGeometryList.end(); ++o) 823 { 824 OGRE_DELETE *o; 825 826 } 827 mOptimisedSubMeshGeometryList.clear(); 828 829 } 830 //-------------------------------------------------------------------------- setVisible(bool visible)831 void InstancedGeometry::setVisible(bool visible) 832 { 833 834 mVisible = visible; 835 // tell any existing BatchInstances 836 for (BatchInstanceMap::iterator ri = mBatchInstanceMap.begin(); 837 ri != mBatchInstanceMap.end(); ++ri) 838 { 839 840 841 ri->second->setVisible(visible); 842 } 843 } 844 //-------------------------------------------------------------------------- setCastShadows(bool castShadows)845 void InstancedGeometry::setCastShadows(bool castShadows) 846 { 847 mCastShadows = castShadows; 848 // tell any existing BatchInstances 849 for (BatchInstanceMap::iterator ri = mBatchInstanceMap.begin(); 850 ri != mBatchInstanceMap.end(); ++ri) 851 { 852 ri->second->setCastShadows(castShadows); 853 } 854 855 } 856 //-------------------------------------------------------------------------- setRenderQueueGroup(uint8 queueID)857 void InstancedGeometry::setRenderQueueGroup(uint8 queueID) 858 { 859 assert(queueID <= RENDER_QUEUE_MAX && "Render queue out of range!"); 860 mRenderQueueIDSet = true; 861 mRenderQueueID = queueID; 862 // tell any existing BatchInstances 863 for (BatchInstanceMap::iterator ri = mBatchInstanceMap.begin(); 864 ri != mBatchInstanceMap.end(); ++ri) 865 { 866 ri->second->setRenderQueueGroup(queueID); 867 } 868 } 869 //-------------------------------------------------------------------------- getRenderQueueGroup(void) const870 uint8 InstancedGeometry::getRenderQueueGroup(void) const 871 { 872 return mRenderQueueID; 873 } 874 //-------------------------------------------------------------------------- dump(const String & filename) const875 void InstancedGeometry::dump(const String& filename) const 876 { 877 std::ofstream of(filename.c_str()); 878 of << "Static Geometry Report for " << mName << std::endl; 879 of << "-------------------------------------------------" << std::endl; 880 of << "Number of queued submeshes: " << mQueuedSubMeshes.size() << std::endl; 881 of << "Number of BatchInstances: " << mBatchInstanceMap.size() << std::endl; 882 of << "BatchInstance dimensions: " << mBatchInstanceDimensions << std::endl; 883 of << "Origin: " << mOrigin << std::endl; 884 of << "Max distance: " << mUpperDistance << std::endl; 885 of << "Casts shadows?: " << mCastShadows << std::endl; 886 of << std::endl; 887 for (BatchInstanceMap::const_iterator ri = mBatchInstanceMap.begin(); 888 ri != mBatchInstanceMap.end(); ++ri) 889 { 890 ri->second->dump(of); 891 } 892 of << "-------------------------------------------------" << std::endl; 893 } 894 //-------------------------------------------------------------------------- setProvideWorldInverses(bool flag)895 void InstancedGeometry::setProvideWorldInverses(bool flag) 896 { 897 mProvideWorldInverses = flag; 898 } 899 //--------------------------------------------------------------------- visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)900 void InstancedGeometry::visitRenderables(Renderable::Visitor* visitor, 901 bool debugRenderables) 902 { 903 for (BatchInstanceMap::const_iterator ri = mBatchInstanceMap.begin(); 904 ri != mBatchInstanceMap.end(); ++ri) 905 { 906 ri->second->visitRenderables(visitor, debugRenderables); 907 } 908 909 } 910 //-------------------------------------------------------------------------- InstancedObject(unsigned short index,SkeletonInstance * skeleton,AnimationStateSet * animations)911 InstancedGeometry::InstancedObject::InstancedObject(unsigned short index,SkeletonInstance *skeleton, AnimationStateSet*animations) 912 : mIndex(index), 913 mTransformation(Matrix4::ZERO), 914 mOrientation(Quaternion::IDENTITY), 915 mScale(Vector3::UNIT_SCALE), 916 mPosition(Vector3::ZERO), 917 mSkeletonInstance(skeleton), 918 mBoneWorldMatrices(NULL), 919 mBoneMatrices(NULL), 920 mNumBoneMatrices(0), 921 mFrameAnimationLastUpdated(std::numeric_limits<unsigned long>::max()) 922 923 { 924 925 mSkeletonInstance->load(); 926 927 mAnimationState = OGRE_NEW AnimationStateSet(); 928 mNumBoneMatrices = mSkeletonInstance->getNumBones(); 929 mBoneMatrices = OGRE_ALLOC_T(Matrix4, mNumBoneMatrices, MEMCATEGORY_ANIMATION); 930 AnimationStateIterator it=animations->getAnimationStateIterator(); 931 while (it.hasMoreElements()) 932 { 933 AnimationState*anim= it.getNext(); 934 mAnimationState->createAnimationState(anim->getAnimationName(),anim->getTimePosition(),anim->getLength(), 935 anim->getWeight()); 936 937 } 938 939 } 940 //-------------------------------------------------------------------------- InstancedObject(unsigned short index)941 InstancedGeometry::InstancedObject::InstancedObject(unsigned short index) 942 :mIndex(index), 943 mTransformation(Matrix4::ZERO), 944 mOrientation(Quaternion::IDENTITY), 945 mScale(Vector3::UNIT_SCALE), 946 mPosition(Vector3::ZERO), 947 mSkeletonInstance(0), 948 mBoneWorldMatrices(0), 949 mBoneMatrices(0), 950 mAnimationState(0), 951 mNumBoneMatrices(0), 952 mFrameAnimationLastUpdated(std::numeric_limits<unsigned long>::max()) 953 { 954 } 955 //-------------------------------------------------------------------------- ~InstancedObject()956 InstancedGeometry::InstancedObject::~InstancedObject() 957 { 958 mGeometryBucketList.clear(); 959 OGRE_DELETE mAnimationState; 960 OGRE_FREE(mBoneMatrices, MEMCATEGORY_ANIMATION); 961 OGRE_FREE(mBoneWorldMatrices, MEMCATEGORY_ANIMATION); 962 } 963 //-------------------------------------------------------------------------- addBucketToList(GeometryBucket * bucket)964 void InstancedGeometry::InstancedObject::addBucketToList(GeometryBucket*bucket) 965 { 966 mGeometryBucketList.push_back(bucket); 967 } 968 //-------------------------------------------------------------------------- setPosition(Vector3 position)969 void InstancedGeometry::InstancedObject::setPosition( Vector3 position) 970 { 971 972 mPosition=position; 973 needUpdate(); 974 BatchInstance*parentBatchInstance=(*(mGeometryBucketList.begin()))->getParent()->getParent()->getParent(); 975 parentBatchInstance->updateBoundingBox(); 976 977 } 978 //-------------------------------------------------------------------------- translate(const Vector3 & d)979 void InstancedGeometry::InstancedObject::translate(const Vector3 &d) 980 { 981 mPosition += d; 982 needUpdate(); 983 } 984 //-------------------------------------------------------------------------- translate(const Matrix3 & axes,const Vector3 & move)985 void InstancedGeometry::InstancedObject::translate(const Matrix3 & axes,const Vector3 &move) 986 { 987 Vector3 derived = axes * move; 988 translate(derived); 989 } 990 //-------------------------------------------------------------------------- getLocalAxes() const991 Matrix3 InstancedGeometry::InstancedObject::getLocalAxes() const 992 { 993 Vector3 axisX = Vector3::UNIT_X; 994 Vector3 axisY = Vector3::UNIT_Y; 995 Vector3 axisZ = Vector3::UNIT_Z; 996 997 axisX = mOrientation * axisX; 998 axisY = mOrientation * axisY; 999 axisZ = mOrientation * axisZ; 1000 1001 return Matrix3(axisX.x, axisY.x, axisZ.x, 1002 axisX.y, axisY.y, axisZ.y, 1003 axisX.z, axisY.z, axisZ.z); 1004 } 1005 //-------------------------------------------------------------------------- getPosition(void) const1006 const Vector3 & InstancedGeometry::InstancedObject::getPosition(void) const 1007 { 1008 return mPosition; 1009 } 1010 //-------------------------------------------------------------------------- yaw(const Radian & angle)1011 void InstancedGeometry::InstancedObject::yaw(const Radian&angle) 1012 { 1013 Quaternion q; 1014 q.FromAngleAxis(angle,Vector3::UNIT_Y); 1015 rotate(q); 1016 } 1017 //-------------------------------------------------------------------------- pitch(const Radian & angle)1018 void InstancedGeometry::InstancedObject::pitch(const Radian&angle) 1019 { 1020 Quaternion q; 1021 q.FromAngleAxis(angle,Vector3::UNIT_X); 1022 rotate(q); 1023 } 1024 //-------------------------------------------------------------------------- roll(const Radian & angle)1025 void InstancedGeometry::InstancedObject::roll(const Radian&angle) 1026 { 1027 Quaternion q; 1028 q.FromAngleAxis(angle,Vector3::UNIT_Z); 1029 rotate(q); 1030 1031 } 1032 //-------------------------------------------------------------------------- setScale(const Vector3 & scale)1033 void InstancedGeometry::InstancedObject::setScale(const Vector3&scale) 1034 { 1035 mScale=scale; 1036 needUpdate(); 1037 } 1038 getScale() const1039 const Vector3& InstancedGeometry::InstancedObject::getScale() const 1040 { 1041 return mScale; 1042 } 1043 1044 //-------------------------------------------------------------------------- rotate(const Quaternion & q)1045 void InstancedGeometry::InstancedObject::rotate(const Quaternion& q) 1046 { 1047 1048 mOrientation = mOrientation * q; 1049 needUpdate(); 1050 } 1051 //-------------------------------------------------------------------------- setOrientation(const Quaternion & q)1052 void InstancedGeometry::InstancedObject::setOrientation(const Quaternion& q) 1053 { 1054 mOrientation = q; 1055 needUpdate(); 1056 } 1057 //-------------------------------------------------------------------------- setPositionAndOrientation(Vector3 p,const Quaternion & q)1058 void InstancedGeometry::InstancedObject::setPositionAndOrientation(Vector3 p, const Quaternion& q) 1059 { 1060 mPosition = p; 1061 mOrientation = q; 1062 needUpdate(); 1063 BatchInstance* parentBatchInstance=(*(mGeometryBucketList.begin()))->getParent()->getParent()->getParent(); 1064 parentBatchInstance->updateBoundingBox(); 1065 } 1066 1067 //-------------------------------------------------------------------------- getOrientation(void)1068 Quaternion &InstancedGeometry::InstancedObject::getOrientation(void) 1069 { 1070 return mOrientation; 1071 } 1072 1073 //-------------------------------------------------------------------------- needUpdate()1074 void InstancedGeometry::InstancedObject::needUpdate() 1075 { 1076 mTransformation.makeTransform( 1077 mPosition, 1078 mScale, 1079 mOrientation); 1080 1081 1082 } 1083 //-------------------------------------------------------------------------- updateAnimation(void)1084 void InstancedGeometry::InstancedObject::updateAnimation(void) 1085 { 1086 1087 if(mSkeletonInstance) 1088 { 1089 GeometryBucketList::iterator it; 1090 mSkeletonInstance->setAnimationState(*mAnimationState); 1091 mSkeletonInstance->_getBoneMatrices(mBoneMatrices); 1092 1093 // Allocate bone world matrices on demand, for better memory footprint 1094 // when using software animation. 1095 if (!mBoneWorldMatrices) 1096 { 1097 mBoneWorldMatrices = OGRE_ALLOC_T(Matrix4, mNumBoneMatrices, MEMCATEGORY_ANIMATION); 1098 } 1099 1100 for (unsigned short i = 0; i < mNumBoneMatrices; ++i) 1101 { 1102 mBoneWorldMatrices[i] = mTransformation * mBoneMatrices[i]; 1103 1104 } 1105 1106 1107 1108 } 1109 1110 } 1111 //-------------------------------------------------------------------------- getAnimationState(const String & name) const1112 AnimationState* InstancedGeometry::InstancedObject::getAnimationState(const String& name) const 1113 { 1114 if (!mAnimationState) 1115 { 1116 OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Object is not animated", 1117 "InstancedGeometry::InstancedObject::getAnimationState"); 1118 } 1119 // AnimationStateIterator it=mAnimationState->getAnimationStateIterator(); 1120 // while (it.hasMoreElements()) 1121 // { 1122 // AnimationState*anim= it.getNext(); 1123 // 1124 // 1125 // } 1126 return mAnimationState->getAnimationState(name); 1127 } 1128 //-------------------------------------------------------------------------- getBatchInstanceIterator(void)1129 InstancedGeometry::BatchInstanceIterator InstancedGeometry::getBatchInstanceIterator(void) 1130 { 1131 return BatchInstanceIterator(mBatchInstanceMap.begin(), mBatchInstanceMap.end()); 1132 } 1133 //-------------------------------------------------------------------------- BatchInstance(InstancedGeometry * parent,const String & name,SceneManager * mgr,uint32 BatchInstanceID)1134 InstancedGeometry::BatchInstance::BatchInstance(InstancedGeometry* parent, const String& name, 1135 SceneManager* mgr, uint32 BatchInstanceID) 1136 : MovableObject(name), mParent(parent), mSceneMgr(mgr), mNode(0), 1137 mBatchInstanceID(BatchInstanceID), mBoundingRadius(0.0f), 1138 mCurrentLod(0), mCamera(0), mLodStrategy(0) 1139 { 1140 } 1141 //-------------------------------------------------------------------------- ~BatchInstance()1142 InstancedGeometry::BatchInstance::~BatchInstance() 1143 { 1144 if (mNode) 1145 { 1146 mNode->getParentSceneNode()->removeChild(mNode); 1147 mSceneMgr->destroySceneNode(mNode->getName()); 1148 mNode = 0; 1149 } 1150 // delete 1151 for (LODBucketList::iterator i = mLodBucketList.begin(); 1152 i != mLodBucketList.end(); ++i) 1153 { 1154 OGRE_DELETE *i; 1155 } 1156 mLodBucketList.clear(); 1157 ObjectsMap::iterator o; 1158 1159 for(o=mInstancesMap.begin();o!=mInstancesMap.end();++o) 1160 { 1161 OGRE_DELETE o->second; 1162 } 1163 mInstancesMap.clear(); 1164 // no need to delete queued meshes, these are managed in InstancedGeometry 1165 } 1166 //-------------------------------------------------------------------------- assign(QueuedSubMesh * qmesh)1167 void InstancedGeometry::BatchInstance::assign(QueuedSubMesh* qmesh) 1168 { 1169 mQueuedSubMeshes.push_back(qmesh); 1170 1171 // Set/check LOD strategy 1172 const LodStrategy *lodStrategy = qmesh->submesh->parent->getLodStrategy(); 1173 if (mLodStrategy == 0) 1174 { 1175 mLodStrategy = lodStrategy; 1176 1177 // First LOD mandatory, and always from base LOD value 1178 mLodValues.push_back(mLodStrategy->getBaseValue()); 1179 } 1180 else 1181 { 1182 if (mLodStrategy != lodStrategy) 1183 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Lod strategies do not match", 1184 "InstancedGeometry::InstancedObject::assign"); 1185 } 1186 1187 // update LOD values 1188 ushort lodLevels = qmesh->submesh->parent->getNumLodLevels(); 1189 assert(qmesh->geometryLodList->size() == lodLevels); 1190 1191 while(mLodValues.size() < lodLevels) 1192 { 1193 mLodValues.push_back(0.0f); 1194 } 1195 // Make sure LOD levels are max of all at the requested level 1196 for (ushort lod = 1; lod < lodLevels; ++lod) 1197 { 1198 const MeshLodUsage& meshLod = 1199 qmesh->submesh->parent->getLodLevel(lod); 1200 mLodValues[lod] = std::max(mLodValues[lod], 1201 meshLod.value); 1202 } 1203 1204 // update bounds 1205 // Transform world bounds relative to our centre 1206 AxisAlignedBox localBounds( 1207 qmesh->worldBounds.getMinimum() , 1208 qmesh->worldBounds.getMaximum()); 1209 mAABB.merge(localBounds); 1210 mBoundingRadius = Math::boundingRadiusFromAABB(mAABB); 1211 1212 } 1213 //-------------------------------------------------------------------------- build()1214 void InstancedGeometry::BatchInstance::build() 1215 { 1216 // Create a node 1217 mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(mName); 1218 mNode->attachObject(this); 1219 // We need to create enough LOD buckets to deal with the highest LOD 1220 // we encountered in all the meshes queued 1221 for (ushort lod = 0; lod < mLodValues.size(); ++lod) 1222 { 1223 LODBucket* lodBucket = 1224 OGRE_NEW LODBucket(this, lod, mLodValues[lod]); 1225 mLodBucketList.push_back(lodBucket); 1226 // Now iterate over the meshes and assign to LODs 1227 // LOD bucket will pick the right LOD to use 1228 QueuedSubMeshList::iterator qi, qiend; 1229 qiend = mQueuedSubMeshes.end(); 1230 for (qi = mQueuedSubMeshes.begin(); qi != qiend; ++qi) 1231 { 1232 lodBucket->assign(*qi, lod); 1233 } 1234 // now build 1235 lodBucket->build(); 1236 } 1237 1238 1239 } 1240 1241 //-------------------------------------------------------------------------- updateBoundingBox()1242 void InstancedGeometry::BatchInstance::updateBoundingBox() 1243 { 1244 AxisAlignedBox aabb; 1245 1246 //Get the first GeometryBucket to get the aabb 1247 LODIterator lodIterator = getLODIterator(); 1248 if( lodIterator.hasMoreElements() ) 1249 { 1250 LODBucket* lod = lodIterator.getNext(); 1251 LODBucket::MaterialIterator matIt = lod->getMaterialIterator(); 1252 if( matIt.hasMoreElements() ) 1253 { 1254 MaterialBucket*mat = matIt.getNext(); 1255 MaterialBucket::GeometryIterator geomIt = mat->getGeometryIterator(); 1256 if( geomIt.hasMoreElements() ) 1257 { 1258 GeometryBucket *geom = geomIt.getNext(); 1259 aabb = geom->getAABB(); 1260 } 1261 } 1262 } 1263 1264 ObjectsMap::iterator objIt; 1265 Vector3 vMin( Vector3::ZERO ); 1266 Vector3 vMax( Vector3::ZERO ); 1267 if( !mInstancesMap.empty() ) 1268 { 1269 objIt = mInstancesMap.begin(); 1270 vMin = objIt->second->getPosition() + aabb.getMinimum(); 1271 vMax = objIt->second->getPosition() + aabb.getMaximum(); 1272 } 1273 1274 for( objIt=mInstancesMap.begin(); objIt!=mInstancesMap.end(); ++objIt ) 1275 { 1276 const Vector3 &position = objIt->second->getPosition(); 1277 const Vector3 &scale = objIt->second->getScale(); 1278 1279 vMin.x = std::min( vMin.x, position.x + aabb.getMinimum().x * scale.x ); 1280 vMin.y = std::min( vMin.y, position.y + aabb.getMinimum().y * scale.y ); 1281 vMin.z = std::min( vMin.z, position.z + aabb.getMinimum().z * scale.z ); 1282 1283 vMax.x = std::max( vMax.x, position.x + aabb.getMaximum().x * scale.x ); 1284 vMax.y = std::max( vMax.y, position.y + aabb.getMaximum().y * scale.y ); 1285 vMax.z = std::max( vMax.z, position.z + aabb.getMaximum().z * scale.z ); 1286 } 1287 1288 aabb.setExtents( vMin, vMax ); 1289 1290 //Now apply the bounding box 1291 lodIterator = getLODIterator(); 1292 while( lodIterator.hasMoreElements() ) 1293 { 1294 LODBucket* lod = lodIterator.getNext(); 1295 LODBucket::MaterialIterator matIt = lod->getMaterialIterator(); 1296 while (matIt.hasMoreElements()) 1297 { 1298 MaterialBucket*mat = matIt.getNext(); 1299 MaterialBucket::GeometryIterator geomIt = mat->getGeometryIterator(); 1300 while( geomIt.hasMoreElements() ) 1301 { 1302 GeometryBucket *geom = geomIt.getNext(); 1303 geom->setBoundingBox( aabb ); 1304 this->mNode->_updateBounds(); 1305 mAABB = aabb; 1306 } 1307 } 1308 } 1309 } 1310 1311 //-------------------------------------------------------------------------- addInstancedObject(unsigned short index,InstancedObject * object)1312 void InstancedGeometry::BatchInstance::addInstancedObject(unsigned short index,InstancedObject* object) 1313 { 1314 mInstancesMap[index]=object; 1315 } 1316 //-------------------------------------------------------------------------- isInstancedObjectPresent(unsigned short index)1317 InstancedGeometry::InstancedObject* InstancedGeometry::BatchInstance::isInstancedObjectPresent(unsigned short index) 1318 { 1319 if (mInstancesMap.find(index)!=mInstancesMap.end()) 1320 return mInstancesMap[index]; 1321 else return NULL; 1322 } 1323 //-------------------------------------------------------------------------- 1324 InstancedGeometry::BatchInstance::InstancedObjectIterator getObjectIterator()1325 InstancedGeometry::BatchInstance::getObjectIterator() 1326 { 1327 return InstancedObjectIterator(mInstancesMap.begin(), mInstancesMap.end()); 1328 } 1329 //-------------------------------------------------------------------------- getMovableType(void) const1330 const String& InstancedGeometry::BatchInstance::getMovableType(void) const 1331 { 1332 static String sType = "InstancedGeometry"; 1333 return sType; 1334 } 1335 //-------------------------------------------------------------------------- _notifyCurrentCamera(Camera * cam)1336 void InstancedGeometry::BatchInstance::_notifyCurrentCamera(Camera* cam) 1337 { 1338 // Set camera 1339 mCamera = cam; 1340 1341 1342 // Cache squared view depth for use by GeometryBucket 1343 mSquaredViewDepth = mParentNode->getSquaredViewDepth(cam->getLodCamera()); 1344 1345 // No LOD strategy set yet, skip (this indicates that there are no submeshes) 1346 if (mLodStrategy == 0) 1347 return; 1348 1349 // Sanity check 1350 assert(!mLodValues.empty()); 1351 1352 // Calculate LOD value 1353 Real lodValue = mLodStrategy->getValue(this, cam); 1354 1355 // Store LOD value for this strategy 1356 mLodValue = lodValue; 1357 1358 // Get LOD index 1359 mCurrentLod = mLodStrategy->getIndex(lodValue, mLodValues); 1360 } 1361 //-------------------------------------------------------------------------- getBoundingBox(void) const1362 const AxisAlignedBox& InstancedGeometry::BatchInstance::getBoundingBox(void) const 1363 { 1364 return mAABB; 1365 } 1366 //-------------------------------------------------------------------------- setBoundingBox(AxisAlignedBox & box)1367 void InstancedGeometry::BatchInstance::setBoundingBox(AxisAlignedBox & box) 1368 { 1369 mAABB=box; 1370 } 1371 //-------------------------------------------------------------------------- getBoundingRadius(void) const1372 Real InstancedGeometry::BatchInstance::getBoundingRadius(void) const 1373 { 1374 return mBoundingRadius; 1375 } 1376 //-------------------------------------------------------------------------- _updateRenderQueue(RenderQueue * queue)1377 void InstancedGeometry::BatchInstance::_updateRenderQueue(RenderQueue* queue) 1378 { 1379 ObjectsMap::iterator it; 1380 //we parse the Instanced Object map to update the animations. 1381 1382 for (it=mInstancesMap.begin();it!=mInstancesMap.end();++it) 1383 { 1384 it->second->updateAnimation(); 1385 1386 } 1387 1388 mLodBucketList[mCurrentLod]->addRenderables(queue, mRenderQueueID, 1389 mLodValue); 1390 } 1391 //--------------------------------------------------------------------- visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)1392 void InstancedGeometry::BatchInstance::visitRenderables( 1393 Renderable::Visitor* visitor, bool debugRenderables) 1394 { 1395 for (LODBucketList::iterator i = mLodBucketList.begin(); i != mLodBucketList.end(); ++i) 1396 { 1397 (*i)->visitRenderables(visitor, debugRenderables); 1398 } 1399 1400 } 1401 //-------------------------------------------------------------------------- isVisible(void) const1402 bool InstancedGeometry::BatchInstance::isVisible(void) const 1403 { 1404 return mVisible && !mBeyondFarDistance; 1405 } 1406 //-------------------------------------------------------------------------- 1407 InstancedGeometry::BatchInstance::LODIterator getLODIterator(void)1408 InstancedGeometry::BatchInstance::getLODIterator(void) 1409 { 1410 return LODIterator(mLodBucketList.begin(), mLodBucketList.end()); 1411 } 1412 //-------------------------------------------------------------------------- getLights(void) const1413 const LightList& InstancedGeometry::BatchInstance::getLights(void) const 1414 { 1415 return queryLights(); 1416 } 1417 //-------------------------------------------------------------------------- dump(std::ofstream & of) const1418 void InstancedGeometry::BatchInstance::dump(std::ofstream& of) const 1419 { 1420 of << "BatchInstance " << mBatchInstanceID << std::endl; 1421 of << "--------------------------" << std::endl; 1422 of << "Local AABB: " << mAABB << std::endl; 1423 of << "Bounding radius: " << mBoundingRadius << std::endl; 1424 of << "Number of LODs: " << mLodBucketList.size() << std::endl; 1425 1426 for (LODBucketList::const_iterator i = mLodBucketList.begin(); 1427 i != mLodBucketList.end(); ++i) 1428 { 1429 (*i)->dump(of); 1430 } 1431 of << "--------------------------" << std::endl; 1432 } 1433 //-------------------------------------------------------------------------- 1434 //-------------------------------------------------------------------------- LODBucket(BatchInstance * parent,unsigned short lod,Real lodValue)1435 InstancedGeometry::LODBucket::LODBucket(BatchInstance* parent, unsigned short lod, 1436 Real lodValue) 1437 : mParent(parent), mLod(lod), mLodValue(lodValue) 1438 { 1439 } 1440 //-------------------------------------------------------------------------- ~LODBucket()1441 InstancedGeometry::LODBucket::~LODBucket() 1442 { 1443 // delete 1444 for (MaterialBucketMap::iterator i = mMaterialBucketMap.begin(); 1445 i != mMaterialBucketMap.end(); ++i) 1446 { 1447 OGRE_DELETE i->second; 1448 } 1449 mMaterialBucketMap.clear(); 1450 for(QueuedGeometryList::iterator qi = mQueuedGeometryList.begin(); 1451 qi != mQueuedGeometryList.end(); ++qi) 1452 { 1453 OGRE_DELETE *qi; 1454 } 1455 mQueuedGeometryList.clear(); 1456 // no need to delete queued meshes, these are managed in InstancedGeometry 1457 } 1458 //-------------------------------------------------------------------------- assign(QueuedSubMesh * qmesh,ushort atLod)1459 void InstancedGeometry::LODBucket::assign(QueuedSubMesh* qmesh, ushort atLod) 1460 { 1461 QueuedGeometry* q = OGRE_NEW QueuedGeometry(); 1462 mQueuedGeometryList.push_back(q); 1463 q->position = qmesh->position; 1464 q->orientation = qmesh->orientation; 1465 q->scale = qmesh->scale; 1466 q->ID = qmesh->ID; 1467 if (qmesh->geometryLodList->size() > atLod) 1468 { 1469 // This submesh has enough lods, use the right one 1470 q->geometry = &(*qmesh->geometryLodList)[atLod]; 1471 } 1472 else 1473 { 1474 // Not enough lods, use the lowest one we have 1475 q->geometry = 1476 &(*qmesh->geometryLodList)[qmesh->geometryLodList->size() - 1]; 1477 } 1478 // Locate a material bucket 1479 MaterialBucket* mbucket = 0; 1480 MaterialBucketMap::iterator m = 1481 mMaterialBucketMap.find(qmesh->materialName); 1482 if (m != mMaterialBucketMap.end()) 1483 { 1484 mbucket = m->second; 1485 } 1486 else 1487 { 1488 mbucket = OGRE_NEW MaterialBucket(this, qmesh->materialName); 1489 mMaterialBucketMap[qmesh->materialName] = mbucket; 1490 } 1491 mbucket->assign(q); 1492 } 1493 //-------------------------------------------------------------------------- build()1494 void InstancedGeometry::LODBucket::build() 1495 { 1496 // Just pass this on to child buckets 1497 1498 for (MaterialBucketMap::iterator i = mMaterialBucketMap.begin(); 1499 i != mMaterialBucketMap.end(); ++i) 1500 { 1501 i->second->build(); 1502 } 1503 } 1504 //-------------------------------------------------------------------------- addRenderables(RenderQueue * queue,uint8 group,Real lodValue)1505 void InstancedGeometry::LODBucket::addRenderables(RenderQueue* queue, 1506 uint8 group, Real lodValue) 1507 { 1508 // Just pass this on to child buckets 1509 MaterialBucketMap::iterator i, iend; 1510 iend = mMaterialBucketMap.end(); 1511 for (i = mMaterialBucketMap.begin(); i != iend; ++i) 1512 { 1513 i->second->addRenderables(queue, group, lodValue); 1514 } 1515 } 1516 //--------------------------------------------------------------------- visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)1517 void InstancedGeometry::LODBucket::visitRenderables(Renderable::Visitor* visitor, 1518 bool debugRenderables) 1519 { 1520 MaterialBucketMap::iterator i, iend; 1521 iend = mMaterialBucketMap.end(); 1522 for (i = mMaterialBucketMap.begin(); i != iend; ++i) 1523 { 1524 i->second->visitRenderables(visitor, debugRenderables); 1525 } 1526 1527 } 1528 //-------------------------------------------------------------------------- 1529 InstancedGeometry::LODBucket::MaterialIterator getMaterialIterator(void)1530 InstancedGeometry::LODBucket::getMaterialIterator(void) 1531 { 1532 return MaterialIterator( 1533 mMaterialBucketMap.begin(), mMaterialBucketMap.end()); 1534 } 1535 //-------------------------------------------------------------------------- dump(std::ofstream & of) const1536 void InstancedGeometry::LODBucket::dump(std::ofstream& of) const 1537 { 1538 of << "LOD Bucket " << mLod << std::endl; 1539 of << "------------------" << std::endl; 1540 of << "Lod Value: " << mLodValue << std::endl; 1541 of << "Number of Materials: " << mMaterialBucketMap.size() << std::endl; 1542 for (MaterialBucketMap::const_iterator i = mMaterialBucketMap.begin(); 1543 i != mMaterialBucketMap.end(); ++i) 1544 { 1545 i->second->dump(of); 1546 } 1547 of << "------------------" << std::endl; 1548 1549 } 1550 //-------------------------------------------------------------------------- 1551 //-------------------------------------------------------------------------- MaterialBucket(LODBucket * parent,const String & materialName)1552 InstancedGeometry::MaterialBucket::MaterialBucket(LODBucket* parent, 1553 const String& materialName) 1554 : mParent(parent) 1555 , mMaterialName(materialName) 1556 , mTechnique(0) 1557 , mLastIndex(0) 1558 { 1559 mMaterial = MaterialManager::getSingleton().getByName(mMaterialName); 1560 } 1561 //-------------------------------------------------------------------------- ~MaterialBucket()1562 InstancedGeometry::MaterialBucket::~MaterialBucket() 1563 { 1564 // delete 1565 for (GeometryBucketList::iterator i = mGeometryBucketList.begin(); 1566 i != mGeometryBucketList.end(); ++i) 1567 { 1568 OGRE_DELETE *i; 1569 } 1570 mGeometryBucketList.clear(); 1571 // no need to delete queued meshes, these are managed in InstancedGeometry 1572 1573 } 1574 //-------------------------------------------------------------------------- 1575 assign(QueuedGeometry * qgeom)1576 void InstancedGeometry::MaterialBucket::assign(QueuedGeometry* qgeom) 1577 { 1578 // Look up any current geometry 1579 String formatString = getGeometryFormatString(qgeom->geometry); 1580 CurrentGeometryMap::iterator gi = mCurrentGeometryMap.find(formatString); 1581 bool newBucket = true; 1582 if (gi != mCurrentGeometryMap.end()) 1583 { 1584 // Found existing geometry, try to assign 1585 newBucket = !gi->second->assign(qgeom); 1586 // Note that this bucket will be replaced as the 'current' 1587 // for this format string below since it's out of space 1588 } 1589 // Do we need to create a new one? 1590 if (newBucket) 1591 { 1592 GeometryBucket* gbucket = OGRE_NEW GeometryBucket(this, formatString, 1593 qgeom->geometry->vertexData, qgeom->geometry->indexData); 1594 // Add to main list 1595 mGeometryBucketList.push_back(gbucket); 1596 // Also index in 'current' list 1597 mCurrentGeometryMap[formatString] = gbucket; 1598 if (!gbucket->assign(qgeom)) 1599 { 1600 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, 1601 "Somehow we couldn't fit the requested geometry even in a " 1602 "brand new GeometryBucket!! Must be a bug, please report.", 1603 "InstancedGeometry::MaterialBucket::assign"); 1604 } 1605 } 1606 } 1607 //-------------------------------------------------------------------------- build()1608 void InstancedGeometry::MaterialBucket::build() 1609 { 1610 mTechnique = 0; 1611 mMaterial = MaterialManager::getSingleton().getByName(mMaterialName); 1612 if (mMaterial.isNull()) 1613 { 1614 OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 1615 "Material '" + mMaterialName + "' not found.", 1616 "InstancedGeometry::MaterialBucket::build"); 1617 } 1618 mMaterial->load(); 1619 // tell the geometry buckets to build 1620 1621 for (GeometryBucketList::iterator i = mGeometryBucketList.begin(); 1622 i != mGeometryBucketList.end(); ++i) 1623 { 1624 (*i)->build(); 1625 } 1626 } 1627 //-------------------------------------------------------------------------- addRenderables(RenderQueue * queue,uint8 group,Real lodValue)1628 void InstancedGeometry::MaterialBucket::addRenderables(RenderQueue* queue, 1629 uint8 group, Real lodValue) 1630 { 1631 // Get batch instance 1632 BatchInstance *batchInstance = mParent->getParent(); 1633 1634 // Get material LOD strategy 1635 const LodStrategy *materialLodStrategy = mMaterial->getLodStrategy(); 1636 1637 // If material strategy doesn't match, recompute LOD value with correct strategy 1638 if (materialLodStrategy != batchInstance->mLodStrategy) 1639 lodValue = materialLodStrategy->getValue(batchInstance, batchInstance->mCamera); 1640 1641 // Determine the current material technique 1642 mTechnique = mMaterial->getBestTechnique( 1643 mMaterial->getLodIndex(lodValue)); 1644 GeometryBucketList::iterator i, iend; 1645 iend = mGeometryBucketList.end(); 1646 1647 for (i = mGeometryBucketList.begin(); i != iend; ++i) 1648 { 1649 queue->addRenderable(*i, group); 1650 } 1651 1652 } 1653 //--------------------------------------------------------------------- visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)1654 void InstancedGeometry::MaterialBucket::visitRenderables( 1655 Renderable::Visitor* visitor, bool debugRenderables) 1656 { 1657 GeometryBucketList::iterator i, iend; 1658 iend = mGeometryBucketList.end(); 1659 for (i = mGeometryBucketList.begin(); i != iend; ++i) 1660 { 1661 (*i)->visitRenderables(visitor, debugRenderables); 1662 } 1663 1664 } 1665 //-------------------------------------------------------------------------- getGeometryFormatString(SubMeshLodGeometryLink * geom)1666 String InstancedGeometry::MaterialBucket::getGeometryFormatString( 1667 SubMeshLodGeometryLink* geom) 1668 { 1669 // Formulate an identifying string for the geometry format 1670 // Must take into account the vertex declaration and the index type 1671 // Format is (all lines separated by '|'): 1672 // Index type 1673 // Vertex element (repeating) 1674 // source 1675 // semantic 1676 // type 1677 StringUtil::StrStreamType str; 1678 1679 str << geom->indexData->indexBuffer->getType() << "|"; 1680 const VertexDeclaration::VertexElementList& elemList = 1681 geom->vertexData->vertexDeclaration->getElements(); 1682 VertexDeclaration::VertexElementList::const_iterator ei, eiend; 1683 eiend = elemList.end(); 1684 for (ei = elemList.begin(); ei != eiend; ++ei) 1685 { 1686 const VertexElement& elem = *ei; 1687 str << elem.getSource() << "|"; 1688 str << elem.getSource() << "|"; 1689 str << elem.getSemantic() << "|"; 1690 str << elem.getType() << "|"; 1691 } 1692 1693 return str.str(); 1694 1695 } 1696 //-------------------------------------------------------------------------- 1697 InstancedGeometry::MaterialBucket::GeometryIterator getGeometryIterator(void)1698 InstancedGeometry::MaterialBucket::getGeometryIterator(void) 1699 { 1700 return GeometryIterator( 1701 mGeometryBucketList.begin(), mGeometryBucketList.end()); 1702 } 1703 //-------------------------------------------------------------------------- dump(std::ofstream & of) const1704 void InstancedGeometry::MaterialBucket::dump(std::ofstream& of) const 1705 { 1706 of << "Material Bucket " << mMaterialName << std::endl; 1707 of << "--------------------------------------------------" << std::endl; 1708 of << "Geometry buckets: " << mGeometryBucketList.size() << std::endl; 1709 for (GeometryBucketList::const_iterator i = mGeometryBucketList.begin(); 1710 i != mGeometryBucketList.end(); ++i) 1711 { 1712 (*i)->dump(of); 1713 } 1714 of << "--------------------------------------------------" << std::endl; 1715 1716 } 1717 //-------------------------------------------------------------------------- 1718 //-------------------------------------------------------------------------- GeometryBucket(MaterialBucket * parent,const String & formatString,const VertexData * vData,const IndexData * iData)1719 InstancedGeometry::GeometryBucket::GeometryBucket(MaterialBucket* parent, 1720 const String& formatString, const VertexData* vData, 1721 const IndexData* iData) 1722 : SimpleRenderable() 1723 , mParent(parent) 1724 , mFormatString(formatString) 1725 , mVertexData(0) 1726 , mIndexData(0) 1727 { 1728 _initGeometryBucket(vData, iData); 1729 } 1730 //-------------------------------------------------------------------------- GeometryBucket(const String & name,MaterialBucket * parent,const String & formatString,const VertexData * vData,const IndexData * iData)1731 InstancedGeometry::GeometryBucket::GeometryBucket(const String& name, MaterialBucket* parent, 1732 const String& formatString, const VertexData* vData, 1733 const IndexData* iData) 1734 : SimpleRenderable(name) 1735 , mParent(parent) 1736 , mFormatString(formatString) 1737 , mVertexData(0) 1738 , mIndexData(0) 1739 { 1740 _initGeometryBucket(vData, iData); 1741 } 1742 //-------------------------------------------------------------------------- GeometryBucket(MaterialBucket * parent,const String & formatString,GeometryBucket * bucket)1743 InstancedGeometry::GeometryBucket::GeometryBucket(MaterialBucket* parent, 1744 const String& formatString,GeometryBucket* bucket) 1745 : SimpleRenderable() 1746 , mParent(parent) 1747 , mFormatString(formatString) 1748 , mVertexData(0) 1749 , mIndexData(0) 1750 { 1751 _initGeometryBucket(bucket); 1752 } 1753 //-------------------------------------------------------------------------- GeometryBucket(const String & name,MaterialBucket * parent,const String & formatString,GeometryBucket * bucket)1754 InstancedGeometry::GeometryBucket::GeometryBucket(const String& name, MaterialBucket* parent, 1755 const String& formatString,GeometryBucket* bucket) 1756 : SimpleRenderable(name) 1757 , mParent(parent) 1758 , mFormatString(formatString) 1759 , mVertexData(0) 1760 , mIndexData(0) 1761 { 1762 _initGeometryBucket(bucket); 1763 } 1764 //-------------------------------------------------------------------------- _initGeometryBucket(const VertexData * vData,const IndexData * iData)1765 void InstancedGeometry::GeometryBucket::_initGeometryBucket(const VertexData* vData, const IndexData* iData) 1766 { 1767 mBatch=mParent->getParent()->getParent()->getParent(); 1768 if(!mBatch->getBaseSkeleton().isNull()) 1769 setCustomParameter(0,Vector4(mBatch->getBaseSkeleton()->getNumBones(),0,0,0)); 1770 //mRenderOperation=OGRE_NEW RenderOperation(); 1771 // Clone the structure from the example 1772 mVertexData = vData->clone(false); 1773 1774 mRenderOp.useIndexes = true; 1775 mRenderOp.indexData = OGRE_NEW IndexData(); 1776 1777 mRenderOp.indexData->indexCount = 0; 1778 mRenderOp.indexData->indexStart = 0; 1779 mRenderOp.vertexData = OGRE_NEW VertexData(); 1780 mRenderOp.vertexData->vertexCount = 0; 1781 1782 // VertexData constructor creates vertexDeclaration; must release to avoid 1783 // memory leak 1784 HardwareBufferManager::getSingleton().destroyVertexDeclaration(mRenderOp.vertexData->vertexDeclaration); 1785 mRenderOp.vertexData->vertexDeclaration = vData->vertexDeclaration->clone(); 1786 mIndexType = iData->indexBuffer->getType(); 1787 // Derive the max vertices 1788 if (mIndexType == HardwareIndexBuffer::IT_32BIT) 1789 { 1790 mMaxVertexIndex = 0xFFFFFFFF; 1791 } 1792 else 1793 { 1794 mMaxVertexIndex = 0xFFFF; 1795 } 1796 1797 1798 size_t offset=0; 1799 unsigned short texCoordOffset=0; 1800 unsigned short texCoordSource=0; 1801 1802 const Ogre::VertexElement*elem=mRenderOp.vertexData->vertexDeclaration->findElementBySemantic(VES_TEXTURE_COORDINATES); 1803 1804 if (elem != NULL) 1805 { 1806 texCoordSource=elem->getSource(); 1807 } 1808 for(ushort i=0;i<mRenderOp.vertexData->vertexDeclaration->getElementCount();i++) 1809 { 1810 if(mRenderOp.vertexData->vertexDeclaration->getElement(i)->getSemantic() == VES_TEXTURE_COORDINATES) 1811 { 1812 texCoordOffset++; 1813 } 1814 if(texCoordSource==mRenderOp.vertexData->vertexDeclaration->getElement(i)->getSource()) 1815 { 1816 offset+= VertexElement::getTypeSize( 1817 mRenderOp.vertexData->vertexDeclaration->getElement(i)->getType()); 1818 } 1819 } 1820 1821 mRenderOp.vertexData->vertexDeclaration->addElement(texCoordSource, offset, VET_FLOAT1, VES_TEXTURE_COORDINATES, texCoordOffset); 1822 mTexCoordIndex = texCoordOffset; 1823 1824 } 1825 //-------------------------------------------------------------------------- _initGeometryBucket(GeometryBucket * bucket)1826 void InstancedGeometry::GeometryBucket::_initGeometryBucket(GeometryBucket* bucket) 1827 { 1828 1829 mBatch=mParent->getParent()->getParent()->getParent(); 1830 if(!mBatch->getBaseSkeleton().isNull()) 1831 setCustomParameter(0,Vector4(mBatch->getBaseSkeleton()->getNumBones(),0,0,0)); 1832 bucket->getRenderOperation(mRenderOp); 1833 mVertexData=mRenderOp.vertexData; 1834 mIndexData=mRenderOp.indexData; 1835 setBoundingBox(AxisAlignedBox(-10000,-10000,-10000, 1836 10000,10000,10000)); 1837 1838 } 1839 //-------------------------------------------------------------------------- ~GeometryBucket()1840 InstancedGeometry::GeometryBucket::~GeometryBucket() 1841 { 1842 } 1843 1844 //-------------------------------------------------------------------------- getBoundingRadius(void) const1845 Real InstancedGeometry::GeometryBucket::getBoundingRadius(void) const 1846 { 1847 return 1; 1848 } 1849 //-------------------------------------------------------------------------- getMaterial(void) const1850 const MaterialPtr& InstancedGeometry::GeometryBucket::getMaterial(void) const 1851 { 1852 return mParent->getMaterial(); 1853 } 1854 //-------------------------------------------------------------------------- getTechnique(void) const1855 Technique* InstancedGeometry::GeometryBucket::getTechnique(void) const 1856 { 1857 return mParent->getCurrentTechnique(); 1858 } 1859 //-------------------------------------------------------------------------- getWorldTransforms(Matrix4 * xform) const1860 void InstancedGeometry::GeometryBucket::getWorldTransforms(Matrix4* xform) const 1861 { 1862 // Should be the identity transform, but lets allow transformation of the 1863 // nodes the BatchInstances are attached to for kicks 1864 if(mBatch->getBaseSkeleton().isNull()) 1865 { 1866 BatchInstance::ObjectsMap::iterator it,itbegin,itend,newit; 1867 itbegin=mParent->getParent()->getParent()->getInstancesMap().begin(); 1868 itend=mParent->getParent()->getParent()->getInstancesMap().end(); 1869 1870 if( mParent->getParent()->getParent()->getParent()->getProvideWorldInverses() ) 1871 { 1872 // For shaders that use normal maps on instanced geometry objects, 1873 // we can pass the world transform inverse matrices alongwith with 1874 // the world matrices. This reduces our usable geometry limit by 1875 // half in each instance. 1876 for (it=itbegin; 1877 it!=itend; 1878 ++it,xform+=2) 1879 { 1880 1881 *xform = it->second->mTransformation; 1882 *(xform+1) = xform->inverse(); 1883 } 1884 } 1885 else 1886 { 1887 for (it=itbegin; 1888 it!=itend; 1889 ++it,xform++) 1890 { 1891 1892 *xform = it->second->mTransformation; 1893 } 1894 } 1895 } 1896 else 1897 { 1898 BatchInstance::ObjectsMap::iterator it,itbegin,itend,newit; 1899 itbegin=mParent->getParent()->getParent()->getInstancesMap().begin(); 1900 itend=mParent->getParent()->getParent()->getInstancesMap().end(); 1901 1902 for (it=itbegin; 1903 it!=itend; 1904 ++it) 1905 { 1906 1907 if( mParent->getParent()->getParent()->getParent()->getProvideWorldInverses() ) 1908 { 1909 for(int i=0;i<it->second->mNumBoneMatrices;++i,xform+=2) 1910 { 1911 *xform = it->second->mBoneWorldMatrices[i]; 1912 *(xform+1) = xform->inverse(); 1913 } 1914 } 1915 else 1916 { 1917 for(int i=0;i<it->second->mNumBoneMatrices;++i,xform++) 1918 { 1919 *xform = it->second->mBoneWorldMatrices[i]; 1920 } 1921 } 1922 } 1923 1924 } 1925 1926 } 1927 //-------------------------------------------------------------------------- getNumWorldTransforms(void) const1928 unsigned short InstancedGeometry::GeometryBucket::getNumWorldTransforms(void) const 1929 { 1930 bool bSendInverseXfrm = mParent->getParent()->getParent()->getParent()->getProvideWorldInverses(); 1931 1932 if(mBatch->getBaseSkeleton().isNull()) 1933 { 1934 BatchInstance* batch=mParent->getParent()->getParent(); 1935 return static_cast<ushort>(batch->getInstancesMap().size() * (bSendInverseXfrm ? 2 : 1)); 1936 } 1937 else 1938 { 1939 BatchInstance* batch=mParent->getParent()->getParent(); 1940 return static_cast<ushort>( 1941 mBatch->getBaseSkeleton()->getNumBones()*batch->getInstancesMap().size() * (bSendInverseXfrm ? 2 : 1)); 1942 } 1943 } 1944 //-------------------------------------------------------------------------- getSquaredViewDepth(const Camera * cam) const1945 Real InstancedGeometry::GeometryBucket::getSquaredViewDepth(const Camera* cam) const 1946 { 1947 const BatchInstance *batchInstance = mParent->getParent()->getParent(); 1948 if (cam == batchInstance->mCamera) 1949 return batchInstance->mSquaredViewDepth; 1950 else 1951 return batchInstance->getParentNode()->getSquaredViewDepth(cam->getLodCamera()); 1952 } 1953 //-------------------------------------------------------------------------- getLights(void) const1954 const LightList& InstancedGeometry::GeometryBucket::getLights(void) const 1955 { 1956 return mParent->getParent()->getParent()->getLights(); 1957 } 1958 //-------------------------------------------------------------------------- getCastsShadows(void) const1959 bool InstancedGeometry::GeometryBucket::getCastsShadows(void) const 1960 { 1961 return mParent->getParent()->getParent()->getCastShadows(); 1962 } 1963 //-------------------------------------------------------------------------- assign(QueuedGeometry * qgeom)1964 bool InstancedGeometry::GeometryBucket::assign(QueuedGeometry* qgeom) 1965 { 1966 // Do we have enough space? 1967 if (mRenderOp.vertexData->vertexCount + qgeom->geometry->vertexData->vertexCount 1968 > mMaxVertexIndex) 1969 { 1970 return false; 1971 } 1972 1973 mQueuedGeometry.push_back(qgeom); 1974 mRenderOp.vertexData->vertexCount += qgeom->geometry->vertexData->vertexCount; 1975 mRenderOp.indexData->indexCount += qgeom->geometry->indexData->indexCount; 1976 1977 return true; 1978 } 1979 1980 //-------------------------------------------------------------------------- build()1981 void InstancedGeometry::GeometryBucket::build() 1982 { 1983 1984 1985 1986 // Ok, here's where we transfer the vertices and indexes to the shared 1987 // buffers 1988 // Shortcuts 1989 VertexDeclaration* dcl = mRenderOp.vertexData->vertexDeclaration; 1990 VertexBufferBinding* binds =mVertexData->vertexBufferBinding; 1991 1992 // create index buffer, and lock 1993 mRenderOp.indexData->indexBuffer = HardwareBufferManager::getSingleton() 1994 .createIndexBuffer(mIndexType, mRenderOp.indexData->indexCount, 1995 HardwareBuffer::HBU_STATIC_WRITE_ONLY); 1996 uint32* p32Dest = 0; 1997 uint16* p16Dest = 0; 1998 1999 if (mIndexType == HardwareIndexBuffer::IT_32BIT) 2000 { 2001 p32Dest = static_cast<uint32*>( 2002 mRenderOp.indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD)); 2003 } 2004 else 2005 { 2006 p16Dest = static_cast<uint16*>( 2007 mRenderOp.indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD)); 2008 } 2009 2010 // create all vertex buffers, and lock 2011 ushort b; 2012 //ushort posBufferIdx = dcl->findElementBySemantic(VES_POSITION)->getSource(); 2013 2014 vector<uchar*>::type destBufferLocks; 2015 vector<VertexDeclaration::VertexElementList>::type bufferElements; 2016 2017 for (b = 0; b < binds->getBufferCount(); ++b) 2018 { 2019 2020 size_t vertexCount = mRenderOp.vertexData->vertexCount; 2021 2022 HardwareVertexBufferSharedPtr vbuf = 2023 HardwareBufferManager::getSingleton().createVertexBuffer( 2024 dcl->getVertexSize(b), 2025 vertexCount, 2026 HardwareBuffer::HBU_STATIC_WRITE_ONLY); 2027 binds->setBinding(b, vbuf); 2028 uchar* pLock = static_cast<uchar*>( 2029 vbuf->lock(HardwareBuffer::HBL_DISCARD)); 2030 destBufferLocks.push_back(pLock); 2031 // Pre-cache vertex elements per buffer 2032 bufferElements.push_back(dcl->findElementsBySource(b)); 2033 mRenderOp.vertexData->vertexBufferBinding->setBinding(b,vbuf); 2034 } 2035 2036 2037 // Iterate over the geometry items 2038 size_t indexOffset = 0; 2039 2040 QueuedGeometryList::iterator gi, giend; 2041 giend = mQueuedGeometry.end(); 2042 2043 // to generate the boundingBox 2044 Real Xmin,Ymin,Zmin,Xmax,Ymax,Zmax; 2045 Xmin=0; 2046 Ymin=0; 2047 Zmin=0; 2048 Xmax=0; 2049 Ymax=0; 2050 Zmax=0; 2051 QueuedGeometry* precGeom = *(mQueuedGeometry.begin()); 2052 unsigned short index=0; 2053 if( mParent->getLastIndex()!=0) 2054 index=mParent->getLastIndex()+1; 2055 2056 for (gi = mQueuedGeometry.begin(); gi != giend; ++gi) 2057 { 2058 2059 QueuedGeometry* geom = *gi; 2060 if(precGeom->ID!=geom->ID) 2061 index++; 2062 2063 //create a new instanced object 2064 InstancedObject* instancedObject = mParent->getParent()->getParent()->isInstancedObjectPresent(index); 2065 if(instancedObject == NULL) 2066 { 2067 if(mBatch->getBaseSkeleton().isNull()) 2068 { 2069 instancedObject= OGRE_NEW InstancedObject(index); 2070 } 2071 else 2072 { 2073 instancedObject= OGRE_NEW InstancedObject(index,mBatch->getBaseSkeletonInstance(), 2074 mBatch->getBaseAnimationState()); 2075 } 2076 mParent->getParent()->getParent()->addInstancedObject(index,instancedObject); 2077 2078 } 2079 instancedObject->addBucketToList(this); 2080 2081 2082 2083 // Copy indexes across with offset 2084 IndexData* srcIdxData = geom->geometry->indexData; 2085 if (mIndexType == HardwareIndexBuffer::IT_32BIT) 2086 { 2087 // Lock source indexes 2088 uint32* pSrc = static_cast<uint32*>( 2089 srcIdxData->indexBuffer->lock( 2090 srcIdxData->indexStart, 2091 srcIdxData->indexCount * srcIdxData->indexBuffer->getIndexSize(), 2092 HardwareBuffer::HBL_READ_ONLY)); 2093 2094 copyIndexes(pSrc, p32Dest, srcIdxData->indexCount, indexOffset); 2095 p32Dest += srcIdxData->indexCount; 2096 srcIdxData->indexBuffer->unlock(); 2097 } 2098 else 2099 { 2100 2101 // Lock source indexes 2102 uint16* pSrc = static_cast<uint16*>( 2103 srcIdxData->indexBuffer->lock( 2104 srcIdxData->indexStart, 2105 srcIdxData->indexCount * srcIdxData->indexBuffer->getIndexSize(), 2106 HardwareBuffer::HBL_READ_ONLY)); 2107 2108 copyIndexes(pSrc, p16Dest, srcIdxData->indexCount, indexOffset); 2109 p16Dest += srcIdxData->indexCount; 2110 srcIdxData->indexBuffer->unlock(); 2111 } 2112 2113 // Now deal with vertex buffers 2114 // we can rely on buffer counts / formats being the same 2115 VertexData* srcVData = geom->geometry->vertexData; 2116 VertexBufferBinding* srcBinds = srcVData->vertexBufferBinding; 2117 2118 for (b = 0; b < binds->getBufferCount(); ++b) 2119 { 2120 2121 // lock source 2122 HardwareVertexBufferSharedPtr srcBuf = 2123 srcBinds->getBuffer(b); 2124 uchar* pSrcBase = static_cast<uchar*>( 2125 srcBuf->lock(HardwareBuffer::HBL_READ_ONLY)); 2126 // Get buffer lock pointer, we'll update this later 2127 uchar* pDstBase = destBufferLocks[b]; 2128 size_t bufInc = srcBuf->getVertexSize(); 2129 2130 // Iterate over vertices 2131 float *pSrcReal, *pDstReal; 2132 Vector3 tmp; 2133 2134 2135 2136 for (size_t v = 0; v < srcVData->vertexCount; ++v) 2137 { 2138 //to know if the current buffer is the one with the buffer or not 2139 bool isTheBufferWithIndex=false; 2140 // Iterate over vertex elements 2141 VertexDeclaration::VertexElementList& elems = 2142 bufferElements[b]; 2143 VertexDeclaration::VertexElementList::iterator ei; 2144 2145 for (ei = elems.begin(); ei != elems.end(); ++ei) 2146 { 2147 VertexElement& elem = *ei; 2148 elem.baseVertexPointerToElement(pSrcBase, &pSrcReal); 2149 elem.baseVertexPointerToElement(pDstBase, &pDstReal); 2150 if(elem.getSemantic()==VES_TEXTURE_COORDINATES && elem.getIndex()==mTexCoordIndex) 2151 { 2152 isTheBufferWithIndex=true; 2153 *pDstReal++ = static_cast<float>(index); 2154 } 2155 else 2156 { 2157 switch (elem.getSemantic()) 2158 { 2159 case VES_POSITION: 2160 tmp.x = pSrcReal[0]; 2161 tmp.y = pSrcReal[1]; 2162 tmp.z = pSrcReal[2]; 2163 if(tmp.x<Xmin) 2164 Xmin = tmp.x; 2165 if(tmp.y<Ymin) 2166 Ymin = tmp.y; 2167 if(tmp.z<Zmin) 2168 Zmin = tmp.z; 2169 if(tmp.x>Xmax) 2170 Xmax = tmp.x; 2171 if(tmp.y>Ymax) 2172 Ymax = tmp.y; 2173 if(tmp.z>Zmax) 2174 Zmax = tmp.z; 2175 default: 2176 // just raw copy 2177 memcpy(pDstReal, pSrcReal, 2178 VertexElement::getTypeSize(elem.getType())); 2179 break; 2180 }; 2181 2182 } 2183 2184 2185 } 2186 if (isTheBufferWithIndex) 2187 pDstBase += bufInc+4; 2188 else 2189 pDstBase += bufInc; 2190 pSrcBase += bufInc; 2191 2192 } 2193 2194 // Update pointer 2195 destBufferLocks[b] = pDstBase; 2196 srcBuf->unlock(); 2197 2198 2199 } 2200 indexOffset += geom->geometry->vertexData->vertexCount; 2201 2202 2203 precGeom=geom; 2204 2205 } 2206 mParent->setLastIndex(index); 2207 // Unlock everything 2208 mRenderOp.indexData->indexBuffer->unlock(); 2209 for (b = 0; b < binds->getBufferCount(); ++b) 2210 { 2211 binds->getBuffer(b)->unlock(); 2212 } 2213 2214 OGRE_DELETE mVertexData; 2215 OGRE_DELETE mIndexData; 2216 2217 mVertexData=mRenderOp.vertexData; 2218 mIndexData=mRenderOp.indexData; 2219 mBatch->getRenderOperationVector().push_back(&mRenderOp); 2220 setBoundingBox(AxisAlignedBox(Xmin,Ymin,Zmin,Xmax,Ymax,Zmax)); 2221 mAABB=AxisAlignedBox(Xmin,Ymin,Zmin,Xmax,Ymax,Zmax); 2222 2223 } 2224 //-------------------------------------------------------------------------- dump(std::ofstream & of) const2225 void InstancedGeometry::GeometryBucket::dump(std::ofstream& of) const 2226 { 2227 of << "Geometry Bucket" << std::endl; 2228 of << "---------------" << std::endl; 2229 of << "Format string: " << mFormatString << std::endl; 2230 of << "Geometry items: " << mQueuedGeometry.size() << std::endl; 2231 of << "---------------" << std::endl; 2232 2233 } 2234 //-------------------------------------------------------------------------- visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)2235 void InstancedGeometry::GeometryBucket::visitRenderables( 2236 Renderable::Visitor* visitor, bool debugRenderables) 2237 { 2238 visitor->visit(this, mParent->getParent()->getLod(), false); 2239 } 2240 //--------------------------------------------------------------------- 2241 2242 } 2243 2244