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 "OgreStaticGeometry.h" 30 #include "OgreEntity.h" 31 #include "OgreEdgeListBuilder.h" 32 #include "OgreLodStrategy.h" 33 #include "OgreIteratorWrappers.h" 34 #include "OgreSubEntity.h" 35 36 namespace Ogre { 37 38 #define REGION_RANGE 1024 39 #define REGION_HALF_RANGE 512 40 #define REGION_MAX_INDEX 511 41 #define REGION_MIN_INDEX -512 42 43 //-------------------------------------------------------------------------- StaticGeometry(SceneManager * owner,const String & name)44 StaticGeometry::StaticGeometry(SceneManager* owner, const String& name): 45 mOwner(owner), 46 mName(name), 47 mBuilt(false), 48 mUpperDistance(0.0f), 49 mSquaredUpperDistance(0.0f), 50 mCastShadows(false), 51 mRegionDimensions(Vector3(1000,1000,1000)), 52 mHalfRegionDimensions(Vector3(500,500,500)), 53 mOrigin(Vector3(0,0,0)), 54 mVisible(true), 55 mRenderQueueID(RENDER_QUEUE_MAIN), 56 mRenderQueueIDSet(false), 57 mVisibilityFlags(Ogre::MovableObject::getDefaultVisibilityFlags()) 58 { 59 } 60 //-------------------------------------------------------------------------- ~StaticGeometry()61 StaticGeometry::~StaticGeometry() 62 { 63 reset(); 64 } 65 //-------------------------------------------------------------------------- getRegion(const AxisAlignedBox & bounds,bool autoCreate)66 StaticGeometry::Region* StaticGeometry::getRegion(const AxisAlignedBox& bounds, 67 bool autoCreate) 68 { 69 if (bounds.isNull()) 70 return 0; 71 72 // Get the region which has the largest overlapping volume 73 const Vector3 min = bounds.getMinimum(); 74 const Vector3 max = bounds.getMaximum(); 75 76 // Get the min and max region indexes 77 ushort minx, miny, minz; 78 ushort maxx, maxy, maxz; 79 getRegionIndexes(min, minx, miny, minz); 80 getRegionIndexes(max, maxx, maxy, maxz); 81 Real maxVolume = 0.0f; 82 ushort finalx = 0, finaly = 0, finalz = 0; 83 for (ushort x = minx; x <= maxx; ++x) 84 { 85 for (ushort y = miny; y <= maxy; ++y) 86 { 87 for (ushort z = minz; z <= maxz; ++z) 88 { 89 Real vol = getVolumeIntersection(bounds, x, y, z); 90 if (vol > maxVolume) 91 { 92 maxVolume = vol; 93 finalx = x; 94 finaly = y; 95 finalz = z; 96 } 97 98 } 99 } 100 } 101 102 assert(maxVolume > 0.0f && 103 "Static geometry: Problem determining closest volume match!"); 104 105 return getRegion(finalx, finaly, finalz, autoCreate); 106 107 } 108 //-------------------------------------------------------------------------- getVolumeIntersection(const AxisAlignedBox & box,ushort x,ushort y,ushort z)109 Real StaticGeometry::getVolumeIntersection(const AxisAlignedBox& box, 110 ushort x, ushort y, ushort z) 111 { 112 // Get bounds of indexed region 113 AxisAlignedBox regionBounds = getRegionBounds(x, y, z); 114 AxisAlignedBox intersectBox = regionBounds.intersection(box); 115 // return a 'volume' which ignores zero dimensions 116 // since we only use this for relative comparisons of the same bounds 117 // this will still be internally consistent 118 Vector3 boxdiff = box.getMaximum() - box.getMinimum(); 119 Vector3 intersectDiff = intersectBox.getMaximum() - intersectBox.getMinimum(); 120 121 return (boxdiff.x == 0 ? 1 : intersectDiff.x) * 122 (boxdiff.y == 0 ? 1 : intersectDiff.y) * 123 (boxdiff.z == 0 ? 1 : intersectDiff.z); 124 125 } 126 //-------------------------------------------------------------------------- getRegionBounds(ushort x,ushort y,ushort z)127 AxisAlignedBox StaticGeometry::getRegionBounds(ushort x, ushort y, ushort z) 128 { 129 Vector3 min( 130 ((Real)x - REGION_HALF_RANGE) * mRegionDimensions.x + mOrigin.x, 131 ((Real)y - REGION_HALF_RANGE) * mRegionDimensions.y + mOrigin.y, 132 ((Real)z - REGION_HALF_RANGE) * mRegionDimensions.z + mOrigin.z 133 ); 134 Vector3 max = min + mRegionDimensions; 135 return AxisAlignedBox(min, max); 136 } 137 //-------------------------------------------------------------------------- getRegionCentre(ushort x,ushort y,ushort z)138 Vector3 StaticGeometry::getRegionCentre(ushort x, ushort y, ushort z) 139 { 140 return Vector3( 141 ((Real)x - REGION_HALF_RANGE) * mRegionDimensions.x + mOrigin.x 142 + mHalfRegionDimensions.x, 143 ((Real)y - REGION_HALF_RANGE) * mRegionDimensions.y + mOrigin.y 144 + mHalfRegionDimensions.y, 145 ((Real)z - REGION_HALF_RANGE) * mRegionDimensions.z + mOrigin.z 146 + mHalfRegionDimensions.z 147 ); 148 } 149 //-------------------------------------------------------------------------- getRegion(ushort x,ushort y,ushort z,bool autoCreate)150 StaticGeometry::Region* StaticGeometry::getRegion( 151 ushort x, ushort y, ushort z, bool autoCreate) 152 { 153 uint32 index = packIndex(x, y, z); 154 Region* ret = getRegion(index); 155 if (!ret && autoCreate) 156 { 157 // Make a name 158 StringStream str; 159 str << mName << ":" << index; 160 // Calculate the region centre 161 Vector3 centre = getRegionCentre(x, y, z); 162 ret = OGRE_NEW Region(this, str.str(), mOwner, index, centre); 163 mOwner->injectMovableObject(ret); 164 ret->setVisible(mVisible); 165 ret->setCastShadows(mCastShadows); 166 if (mRenderQueueIDSet) 167 { 168 ret->setRenderQueueGroup(mRenderQueueID); 169 } 170 mRegionMap[index] = ret; 171 } 172 return ret; 173 } 174 //-------------------------------------------------------------------------- getRegion(uint32 index)175 StaticGeometry::Region* StaticGeometry::getRegion(uint32 index) 176 { 177 RegionMap::iterator i = mRegionMap.find(index); 178 if (i != mRegionMap.end()) 179 { 180 return i->second; 181 } 182 else 183 { 184 return 0; 185 } 186 187 } 188 //-------------------------------------------------------------------------- getRegionIndexes(const Vector3 & point,ushort & x,ushort & y,ushort & z)189 void StaticGeometry::getRegionIndexes(const Vector3& point, 190 ushort& x, ushort& y, ushort& z) 191 { 192 // Scale the point into multiples of region and adjust for origin 193 Vector3 scaledPoint = (point - mOrigin) / mRegionDimensions; 194 195 // Round down to 'bottom left' point which represents the cell index 196 int ix = Math::IFloor(scaledPoint.x); 197 int iy = Math::IFloor(scaledPoint.y); 198 int iz = Math::IFloor(scaledPoint.z); 199 200 // Check bounds 201 if (ix < REGION_MIN_INDEX || ix > REGION_MAX_INDEX 202 || iy < REGION_MIN_INDEX || iy > REGION_MAX_INDEX 203 || iz < REGION_MIN_INDEX || iz > REGION_MAX_INDEX) 204 { 205 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 206 "Point out of bounds", 207 "StaticGeometry::getRegionIndexes"); 208 } 209 // Adjust for the fact that we use unsigned values for simplicity 210 // (requires less faffing about for negatives give 10-bit packing 211 x = static_cast<ushort>(ix + REGION_HALF_RANGE); 212 y = static_cast<ushort>(iy + REGION_HALF_RANGE); 213 z = static_cast<ushort>(iz + REGION_HALF_RANGE); 214 215 216 } 217 //-------------------------------------------------------------------------- packIndex(ushort x,ushort y,ushort z)218 uint32 StaticGeometry::packIndex(ushort x, ushort y, ushort z) 219 { 220 return x + (y << 10) + (z << 20); 221 } 222 //-------------------------------------------------------------------------- getRegion(const Vector3 & point,bool autoCreate)223 StaticGeometry::Region* StaticGeometry::getRegion(const Vector3& point, 224 bool autoCreate) 225 { 226 ushort x, y, z; 227 getRegionIndexes(point, x, y, z); 228 return getRegion(x, y, z, autoCreate); 229 } 230 //-------------------------------------------------------------------------- calculateBounds(VertexData * vertexData,const Vector3 & position,const Quaternion & orientation,const Vector3 & scale)231 AxisAlignedBox StaticGeometry::calculateBounds(VertexData* vertexData, 232 const Vector3& position, const Quaternion& orientation, 233 const Vector3& scale) 234 { 235 const VertexElement* posElem = 236 vertexData->vertexDeclaration->findElementBySemantic( 237 VES_POSITION); 238 HardwareVertexBufferSharedPtr vbuf = 239 vertexData->vertexBufferBinding->getBuffer(posElem->getSource()); 240 HardwareBufferLockGuard vbufLock(vbuf, HardwareBuffer::HBL_READ_ONLY); 241 unsigned char* vertex = static_cast<unsigned char*>(vbufLock.pData); 242 float* pFloat; 243 244 Vector3 min = Vector3::ZERO, max = Vector3::UNIT_SCALE; 245 bool first = true; 246 247 for(size_t j = 0; j < vertexData->vertexCount; ++j, vertex += vbuf->getVertexSize()) 248 { 249 posElem->baseVertexPointerToElement(vertex, &pFloat); 250 251 Vector3 pt; 252 253 pt.x = (*pFloat++); 254 pt.y = (*pFloat++); 255 pt.z = (*pFloat++); 256 // Transform to world (scale, rotate, translate) 257 pt = (orientation * (pt * scale)) + position; 258 if (first) 259 { 260 min = max = pt; 261 first = false; 262 } 263 else 264 { 265 min.makeFloor(pt); 266 max.makeCeil(pt); 267 } 268 269 } 270 return AxisAlignedBox(min, max); 271 } 272 //-------------------------------------------------------------------------- addEntity(Entity * ent,const Vector3 & position,const Quaternion & orientation,const Vector3 & scale)273 void StaticGeometry::addEntity(Entity* ent, const Vector3& position, 274 const Quaternion& orientation, const Vector3& scale) 275 { 276 const MeshPtr& msh = ent->getMesh(); 277 // Validate 278 if (msh->hasManualLodLevel()) 279 { 280 LogManager::getSingleton().logWarning("(StaticGeometry): Manual LOD is not supported. " 281 "Using only highest LOD level for mesh " + 282 msh->getName()); 283 } 284 285 AxisAlignedBox sharedWorldBounds; 286 // queue this entities submeshes and choice of material 287 // also build the lists of geometry to be used for the source of lods 288 for (uint i = 0; i < ent->getNumSubEntities(); ++i) 289 { 290 SubEntity* se = ent->getSubEntity(i); 291 QueuedSubMesh* q = OGRE_NEW QueuedSubMesh(); 292 293 // Get the geometry for this SubMesh 294 q->submesh = se->getSubMesh(); 295 q->geometryLodList = determineGeometry(q->submesh); 296 q->materialName = se->getMaterialName(); 297 q->orientation = orientation; 298 q->position = position; 299 q->scale = scale; 300 // Determine the bounds based on the highest LOD 301 q->worldBounds = calculateBounds( 302 (*q->geometryLodList)[0].vertexData, 303 position, orientation, scale); 304 305 mQueuedSubMeshes.push_back(q); 306 } 307 } 308 //-------------------------------------------------------------------------- 309 StaticGeometry::SubMeshLodGeometryLinkList* determineGeometry(SubMesh * sm)310 StaticGeometry::determineGeometry(SubMesh* sm) 311 { 312 // First, determine if we've already seen this submesh before 313 SubMeshGeometryLookup::iterator i = 314 mSubMeshGeometryLookup.find(sm); 315 if (i != mSubMeshGeometryLookup.end()) 316 { 317 return i->second; 318 } 319 // Otherwise, we have to create a new one 320 SubMeshLodGeometryLinkList* lodList = OGRE_NEW_T(SubMeshLodGeometryLinkList, MEMCATEGORY_GEOMETRY)(); 321 mSubMeshGeometryLookup[sm] = lodList; 322 ushort numLods = sm->parent->hasManualLodLevel() ? 1 : 323 sm->parent->getNumLodLevels(); 324 lodList->resize(numLods); 325 for (ushort lod = 0; lod < numLods; ++lod) 326 { 327 SubMeshLodGeometryLink& geomLink = (*lodList)[lod]; 328 IndexData *lodIndexData; 329 if (lod == 0) 330 { 331 lodIndexData = sm->indexData; 332 } 333 else 334 { 335 lodIndexData = sm->mLodFaceList[lod - 1]; 336 } 337 // Can use the original mesh geometry? 338 if (sm->useSharedVertices) 339 { 340 if (sm->parent->getNumSubMeshes() == 1) 341 { 342 // Ok, this is actually our own anyway 343 geomLink.vertexData = sm->parent->sharedVertexData; 344 geomLink.indexData = lodIndexData; 345 } 346 else 347 { 348 // We have to split it 349 splitGeometry(sm->parent->sharedVertexData, 350 lodIndexData, &geomLink); 351 } 352 } 353 else 354 { 355 if (lod == 0) 356 { 357 // Ok, we can use the existing geometry; should be in full 358 // use by just this SubMesh 359 geomLink.vertexData = sm->vertexData; 360 geomLink.indexData = sm->indexData; 361 } 362 else 363 { 364 // We have to split it 365 splitGeometry(sm->vertexData, 366 lodIndexData, &geomLink); 367 } 368 } 369 assert (geomLink.vertexData->vertexStart == 0 && 370 "Cannot use vertexStart > 0 on indexed geometry due to " 371 "rendersystem incompatibilities - see the docs!"); 372 } 373 374 375 return lodList; 376 } 377 //-------------------------------------------------------------------------- splitGeometry(VertexData * vd,IndexData * id,StaticGeometry::SubMeshLodGeometryLink * targetGeomLink)378 void StaticGeometry::splitGeometry(VertexData* vd, IndexData* id, 379 StaticGeometry::SubMeshLodGeometryLink* targetGeomLink) 380 { 381 // Firstly we need to scan to see how many vertices are being used 382 // and while we're at it, build the remap we can use later 383 bool use32bitIndexes = 384 id->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT; 385 IndexRemap indexRemap; 386 HardwareBufferLockGuard indexLock(id->indexBuffer, 387 id->indexStart * id->indexBuffer->getIndexSize(), 388 id->indexCount * id->indexBuffer->getIndexSize(), 389 HardwareBuffer::HBL_READ_ONLY); 390 if (use32bitIndexes) 391 { 392 buildIndexRemap(static_cast<uint32*>(indexLock.pData), id->indexCount, indexRemap); 393 } 394 else 395 { 396 buildIndexRemap(static_cast<uint16*>(indexLock.pData), id->indexCount, indexRemap); 397 } 398 indexLock.unlock(); 399 if (indexRemap.size() == vd->vertexCount) 400 { 401 // ha, complete usage after all 402 targetGeomLink->vertexData = vd; 403 targetGeomLink->indexData = id; 404 return; 405 } 406 407 408 // Create the new vertex data records 409 targetGeomLink->vertexData = vd->clone(false); 410 // Convenience 411 VertexData* newvd = targetGeomLink->vertexData; 412 //IndexData* newid = targetGeomLink->indexData; 413 // Update the vertex count 414 newvd->vertexCount = indexRemap.size(); 415 416 size_t numvbufs = vd->vertexBufferBinding->getBufferCount(); 417 // Copy buffers from old to new 418 for (unsigned short b = 0; b < numvbufs; ++b) 419 { 420 // Lock old buffer 421 HardwareVertexBufferSharedPtr oldBuf = 422 vd->vertexBufferBinding->getBuffer(b); 423 // Create new buffer 424 HardwareVertexBufferSharedPtr newBuf = 425 HardwareBufferManager::getSingleton().createVertexBuffer( 426 oldBuf->getVertexSize(), 427 indexRemap.size(), 428 HardwareBuffer::HBU_STATIC); 429 // rebind 430 newvd->vertexBufferBinding->setBinding(b, newBuf); 431 432 // Copy all the elements of the buffer across, by iterating over 433 // the IndexRemap which describes how to move the old vertices 434 // to the new ones. By nature of the map the remap is in order of 435 // indexes in the old buffer, but note that we're not guaranteed to 436 // address every vertex (which is kinda why we're here) 437 HardwareBufferLockGuard oldBufLock(oldBuf, HardwareBuffer::HBL_READ_ONLY); 438 HardwareBufferLockGuard newBufLock(newBuf, HardwareBuffer::HBL_DISCARD); 439 size_t vertexSize = oldBuf->getVertexSize(); 440 // Buffers should be the same size 441 assert (vertexSize == newBuf->getVertexSize()); 442 443 for (IndexRemap::iterator r = indexRemap.begin(); 444 r != indexRemap.end(); ++r) 445 { 446 assert (r->first < oldBuf->getNumVertices()); 447 assert (r->second < newBuf->getNumVertices()); 448 449 uchar* pSrc = static_cast<uchar*>(oldBufLock.pData) + r->first * vertexSize; 450 uchar* pDst = static_cast<uchar*>(newBufLock.pData) + r->second * vertexSize; 451 memcpy(pDst, pSrc, vertexSize); 452 } 453 } 454 455 // Now create a new index buffer 456 HardwareIndexBufferSharedPtr ibuf = 457 HardwareBufferManager::getSingleton().createIndexBuffer( 458 id->indexBuffer->getType(), id->indexCount, 459 HardwareBuffer::HBU_STATIC); 460 461 HardwareBufferLockGuard srcIndexLock(id->indexBuffer, 462 id->indexStart * id->indexBuffer->getIndexSize(), 463 id->indexCount * id->indexBuffer->getIndexSize(), 464 HardwareBuffer::HBL_READ_ONLY); 465 HardwareBufferLockGuard dstIndexLock(ibuf, HardwareBuffer::HBL_DISCARD); 466 if (use32bitIndexes) 467 { 468 uint32 *pSrc32 = static_cast<uint32*>(srcIndexLock.pData); 469 uint32 *pDst32 = static_cast<uint32*>(dstIndexLock.pData); 470 remapIndexes(pSrc32, pDst32, indexRemap, id->indexCount); 471 } 472 else 473 { 474 uint16 *pSrc16 = static_cast<uint16*>(srcIndexLock.pData); 475 uint16 *pDst16 = static_cast<uint16*>(dstIndexLock.pData); 476 remapIndexes(pSrc16, pDst16, indexRemap, id->indexCount); 477 } 478 srcIndexLock.unlock(); 479 dstIndexLock.unlock(); 480 481 targetGeomLink->indexData = OGRE_NEW IndexData(); 482 targetGeomLink->indexData->indexStart = 0; 483 targetGeomLink->indexData->indexCount = id->indexCount; 484 targetGeomLink->indexData->indexBuffer = ibuf; 485 486 // Store optimised geometry for deallocation later 487 OptimisedSubMeshGeometry *optGeom = OGRE_NEW OptimisedSubMeshGeometry(); 488 optGeom->indexData = targetGeomLink->indexData; 489 optGeom->vertexData = targetGeomLink->vertexData; 490 mOptimisedSubMeshGeometryList.push_back(optGeom); 491 } 492 //-------------------------------------------------------------------------- addSceneNode(const SceneNode * node)493 void StaticGeometry::addSceneNode(const SceneNode* node) 494 { 495 for (auto mobj : node->getAttachedObjects()) 496 { 497 if (mobj->getMovableType() == "Entity") 498 { 499 addEntity(static_cast<Entity*>(mobj), 500 node->_getDerivedPosition(), 501 node->_getDerivedOrientation(), 502 node->_getDerivedScale()); 503 } 504 } 505 // Iterate through all the child-nodes 506 for (auto c : node->getChildren()) 507 { 508 // Add this subnode and its children... 509 addSceneNode( static_cast<const SceneNode*>(c) ); 510 } 511 } 512 //-------------------------------------------------------------------------- build(void)513 void StaticGeometry::build(void) 514 { 515 // Make sure there's nothing from previous builds 516 destroy(); 517 518 // Firstly allocate meshes to regions 519 for (QueuedSubMeshList::iterator qi = mQueuedSubMeshes.begin(); 520 qi != mQueuedSubMeshes.end(); ++qi) 521 { 522 QueuedSubMesh* qsm = *qi; 523 Region* region = getRegion(qsm->worldBounds, true); 524 region->assign(qsm); 525 } 526 bool stencilShadows = false; 527 if (mCastShadows && mOwner->isShadowTechniqueStencilBased()) 528 { 529 stencilShadows = true; 530 } 531 532 // Now tell each region to build itself 533 for (RegionMap::iterator ri = mRegionMap.begin(); 534 ri != mRegionMap.end(); ++ri) 535 { 536 ri->second->build(stencilShadows); 537 538 // Set the visibility flags on these regions 539 ri->second->setVisibilityFlags(mVisibilityFlags); 540 } 541 542 } 543 //-------------------------------------------------------------------------- destroy(void)544 void StaticGeometry::destroy(void) 545 { 546 // delete the regions 547 for (RegionMap::iterator i = mRegionMap.begin(); 548 i != mRegionMap.end(); ++i) 549 { 550 mOwner->extractMovableObject(i->second); 551 OGRE_DELETE i->second; 552 } 553 mRegionMap.clear(); 554 } 555 //-------------------------------------------------------------------------- reset(void)556 void StaticGeometry::reset(void) 557 { 558 destroy(); 559 for (QueuedSubMeshList::iterator i = mQueuedSubMeshes.begin(); 560 i != mQueuedSubMeshes.end(); ++i) 561 { 562 OGRE_DELETE *i; 563 } 564 mQueuedSubMeshes.clear(); 565 // Delete precached geoemtry lists 566 for (SubMeshGeometryLookup::iterator l = mSubMeshGeometryLookup.begin(); 567 l != mSubMeshGeometryLookup.end(); ++l) 568 { 569 OGRE_DELETE_T(l->second, SubMeshLodGeometryLinkList, MEMCATEGORY_GEOMETRY); 570 } 571 mSubMeshGeometryLookup.clear(); 572 // Delete optimised geometry 573 for (OptimisedSubMeshGeometryList::iterator o = mOptimisedSubMeshGeometryList.begin(); 574 o != mOptimisedSubMeshGeometryList.end(); ++o) 575 { 576 OGRE_DELETE *o; 577 } 578 mOptimisedSubMeshGeometryList.clear(); 579 580 } 581 //-------------------------------------------------------------------------- setVisible(bool visible)582 void StaticGeometry::setVisible(bool visible) 583 { 584 mVisible = visible; 585 // tell any existing regions 586 for (RegionMap::iterator ri = mRegionMap.begin(); 587 ri != mRegionMap.end(); ++ri) 588 { 589 ri->second->setVisible(visible); 590 } 591 } 592 //-------------------------------------------------------------------------- setCastShadows(bool castShadows)593 void StaticGeometry::setCastShadows(bool castShadows) 594 { 595 mCastShadows = castShadows; 596 // tell any existing regions 597 for (RegionMap::iterator ri = mRegionMap.begin(); 598 ri != mRegionMap.end(); ++ri) 599 { 600 ri->second->setCastShadows(castShadows); 601 } 602 603 } 604 //-------------------------------------------------------------------------- setRenderQueueGroup(uint8 queueID)605 void StaticGeometry::setRenderQueueGroup(uint8 queueID) 606 { 607 assert(queueID <= RENDER_QUEUE_MAX && "Render queue out of range!"); 608 mRenderQueueIDSet = true; 609 mRenderQueueID = queueID; 610 // tell any existing regions 611 for (RegionMap::iterator ri = mRegionMap.begin(); 612 ri != mRegionMap.end(); ++ri) 613 { 614 ri->second->setRenderQueueGroup(queueID); 615 } 616 } 617 //-------------------------------------------------------------------------- getRenderQueueGroup(void) const618 uint8 StaticGeometry::getRenderQueueGroup(void) const 619 { 620 return mRenderQueueID; 621 } 622 //-------------------------------------------------------------------------- setVisibilityFlags(uint32 flags)623 void StaticGeometry::setVisibilityFlags(uint32 flags) 624 { 625 mVisibilityFlags = flags; 626 for (RegionMap::const_iterator ri = mRegionMap.begin(); 627 ri != mRegionMap.end(); ++ri) 628 { 629 ri->second->setVisibilityFlags(flags); 630 } 631 } 632 //-------------------------------------------------------------------------- getVisibilityFlags() const633 uint32 StaticGeometry::getVisibilityFlags() const 634 { 635 if(mRegionMap.empty()) 636 return MovableObject::getDefaultVisibilityFlags(); 637 638 RegionMap::const_iterator ri = mRegionMap.begin(); 639 return ri->second->getVisibilityFlags(); 640 } 641 //-------------------------------------------------------------------------- dump(const String & filename) const642 void StaticGeometry::dump(const String& filename) const 643 { 644 std::ofstream of(filename.c_str()); 645 of << "Static Geometry Report for " << mName << std::endl; 646 of << "-------------------------------------------------" << std::endl; 647 of << "Number of queued submeshes: " << mQueuedSubMeshes.size() << std::endl; 648 of << "Number of regions: " << mRegionMap.size() << std::endl; 649 of << "Region dimensions: " << mRegionDimensions << std::endl; 650 of << "Origin: " << mOrigin << std::endl; 651 of << "Max distance: " << mUpperDistance << std::endl; 652 of << "Casts shadows?: " << mCastShadows << std::endl; 653 of << std::endl; 654 for (RegionMap::const_iterator ri = mRegionMap.begin(); 655 ri != mRegionMap.end(); ++ri) 656 { 657 ri->second->dump(of); 658 } 659 of << "-------------------------------------------------" << std::endl; 660 } 661 //--------------------------------------------------------------------- visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)662 void StaticGeometry::visitRenderables(Renderable::Visitor* visitor, 663 bool debugRenderables) 664 { 665 for (RegionMap::const_iterator ri = mRegionMap.begin(); 666 ri != mRegionMap.end(); ++ri) 667 { 668 ri->second->visitRenderables(visitor, debugRenderables); 669 } 670 671 } 672 //-------------------------------------------------------------------------- getRegionIterator(void)673 StaticGeometry::RegionIterator StaticGeometry::getRegionIterator(void) 674 { 675 return RegionIterator(mRegionMap.begin(), mRegionMap.end()); 676 } 677 //-------------------------------------------------------------------------- 678 //-------------------------------------------------------------------------- Region(StaticGeometry * parent,const String & name,SceneManager * mgr,uint32 regionID,const Vector3 & centre)679 StaticGeometry::Region::Region(StaticGeometry* parent, const String& name, 680 SceneManager* mgr, uint32 regionID, const Vector3& centre) 681 : MovableObject(name), mParent(parent), mSceneMgr(mgr), mNode(0), 682 mRegionID(regionID), mCentre(centre), mBoundingRadius(0.0f), 683 mCurrentLod(0), mLodStrategy(0), mCamera(0), mSquaredViewDepth(0) 684 { 685 } 686 //-------------------------------------------------------------------------- ~Region()687 StaticGeometry::Region::~Region() 688 { 689 if (mNode) 690 { 691 mNode->getParentSceneNode()->removeChild(mNode); 692 mSceneMgr->destroySceneNode(mNode->getName()); 693 mNode = 0; 694 } 695 // delete 696 for (LODBucketList::iterator i = mLodBucketList.begin(); 697 i != mLodBucketList.end(); ++i) 698 { 699 OGRE_DELETE *i; 700 } 701 mLodBucketList.clear(); 702 703 // no need to delete queued meshes, these are managed in StaticGeometry 704 705 } 706 //----------------------------------------------------------------------- _releaseManualHardwareResources()707 void StaticGeometry::Region::_releaseManualHardwareResources() 708 { 709 for (LODBucketList::iterator i = mLodBucketList.begin(); i != mLodBucketList.end(); ++i) 710 { 711 clearShadowRenderableList((*i)->getShadowRenderableList()); 712 } 713 } 714 //----------------------------------------------------------------------- _restoreManualHardwareResources()715 void StaticGeometry::Region::_restoreManualHardwareResources() 716 { 717 // shadow renderables are lazy initialized 718 } 719 //-------------------------------------------------------------------------- getTypeFlags(void) const720 uint32 StaticGeometry::Region::getTypeFlags(void) const 721 { 722 return SceneManager::STATICGEOMETRY_TYPE_MASK; 723 } 724 //-------------------------------------------------------------------------- assign(QueuedSubMesh * qmesh)725 void StaticGeometry::Region::assign(QueuedSubMesh* qmesh) 726 { 727 mQueuedSubMeshes.push_back(qmesh); 728 729 // Set/check LOD strategy 730 const LodStrategy *lodStrategy = qmesh->submesh->parent->getLodStrategy(); 731 if (mLodStrategy == 0) 732 { 733 mLodStrategy = lodStrategy; 734 735 // First LOD mandatory, and always from base LOD value 736 mLodValues.push_back(mLodStrategy->getBaseValue()); 737 } 738 else 739 { 740 if (mLodStrategy != lodStrategy) 741 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Lod strategies do not match", 742 "StaticGeometry::Region::assign"); 743 } 744 745 // update LOD values 746 ushort lodLevels = qmesh->submesh->parent->getNumLodLevels(); 747 assert(qmesh->geometryLodList->size() == lodLevels); 748 749 while(mLodValues.size() < lodLevels) 750 { 751 mLodValues.push_back(0.0f); 752 } 753 // Make sure LOD levels are max of all at the requested level 754 for (ushort lod = 1; lod < lodLevels; ++lod) 755 { 756 const MeshLodUsage& meshLod = 757 qmesh->submesh->parent->getLodLevel(lod); 758 mLodValues[lod] = std::max(mLodValues[lod], 759 meshLod.value); 760 } 761 762 // update bounds 763 // Transform world bounds relative to our centre 764 AxisAlignedBox localBounds( 765 qmesh->worldBounds.getMinimum() - mCentre, 766 qmesh->worldBounds.getMaximum() - mCentre); 767 mAABB.merge(localBounds); 768 mBoundingRadius = Math::boundingRadiusFromAABB(mAABB); 769 770 } 771 //-------------------------------------------------------------------------- build(bool stencilShadows)772 void StaticGeometry::Region::build(bool stencilShadows) 773 { 774 // Create a node 775 mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(mName, 776 mCentre); 777 mNode->attachObject(this); 778 // We need to create enough LOD buckets to deal with the highest LOD 779 // we encountered in all the meshes queued 780 for (ushort lod = 0; lod < mLodValues.size(); ++lod) 781 { 782 LODBucket* lodBucket = 783 OGRE_NEW LODBucket(this, lod, mLodValues[lod]); 784 mLodBucketList.push_back(lodBucket); 785 // Now iterate over the meshes and assign to LODs 786 // LOD bucket will pick the right LOD to use 787 QueuedSubMeshList::iterator qi, qiend; 788 qiend = mQueuedSubMeshes.end(); 789 for (qi = mQueuedSubMeshes.begin(); qi != qiend; ++qi) 790 { 791 lodBucket->assign(*qi, lod); 792 } 793 // now build 794 lodBucket->build(stencilShadows); 795 } 796 797 798 799 } 800 //-------------------------------------------------------------------------- getMovableType(void) const801 const String& StaticGeometry::Region::getMovableType(void) const 802 { 803 static String sType = "StaticGeometry"; 804 return sType; 805 } 806 //-------------------------------------------------------------------------- _notifyCurrentCamera(Camera * cam)807 void StaticGeometry::Region::_notifyCurrentCamera(Camera* cam) 808 { 809 // Set camera 810 mCamera = cam; 811 812 // Cache squared view depth for use by GeometryBucket 813 mSquaredViewDepth = mParentNode->getSquaredViewDepth(cam->getLodCamera()); 814 815 // No LOD strategy set yet, skip (this indicates that there are no submeshes) 816 if (mLodStrategy == 0) 817 return; 818 819 // Sanity check 820 assert(!mLodValues.empty()); 821 822 // Calculate LOD value 823 Real lodValue = mLodStrategy->getValue(this, cam); 824 825 // Store LOD value for this strategy 826 mLodValue = lodValue; 827 828 // Get LOD index 829 mCurrentLod = mLodStrategy->getIndex(lodValue, mLodValues); 830 } 831 //-------------------------------------------------------------------------- getBoundingBox(void) const832 const AxisAlignedBox& StaticGeometry::Region::getBoundingBox(void) const 833 { 834 return mAABB; 835 } 836 //-------------------------------------------------------------------------- getBoundingRadius(void) const837 Real StaticGeometry::Region::getBoundingRadius(void) const 838 { 839 return mBoundingRadius; 840 } 841 //-------------------------------------------------------------------------- _updateRenderQueue(RenderQueue * queue)842 void StaticGeometry::Region::_updateRenderQueue(RenderQueue* queue) 843 { 844 mLodBucketList[mCurrentLod]->addRenderables(queue, mRenderQueueID, 845 mLodValue); 846 } 847 //--------------------------------------------------------------------- visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)848 void StaticGeometry::Region::visitRenderables(Renderable::Visitor* visitor, 849 bool debugRenderables) 850 { 851 for (LODBucketList::iterator i = mLodBucketList.begin(); i != mLodBucketList.end(); ++i) 852 { 853 (*i)->visitRenderables(visitor, debugRenderables); 854 } 855 856 } 857 //-------------------------------------------------------------------------- isVisible(void) const858 bool StaticGeometry::Region::isVisible(void) const 859 { 860 if(!mVisible || mBeyondFarDistance) 861 return false; 862 863 SceneManager* sm = Root::getSingleton()._getCurrentSceneManager(); 864 if (sm && !(mVisibilityFlags & sm->_getCombinedVisibilityMask())) 865 return false; 866 867 return true; 868 } 869 //-------------------------------------------------------------------------- 870 StaticGeometry::Region::LODIterator getLODIterator(void)871 StaticGeometry::Region::getLODIterator(void) 872 { 873 return LODIterator(mLodBucketList.begin(), mLodBucketList.end()); 874 } 875 //--------------------------------------------------------------------- 876 ShadowCaster::ShadowRenderableListIterator getShadowVolumeRenderableIterator(ShadowTechnique shadowTechnique,const Light * light,HardwareIndexBufferSharedPtr * indexBuffer,size_t * indexBufferUsedSize,bool extrude,Real extrusionDistance,unsigned long flags)877 StaticGeometry::Region::getShadowVolumeRenderableIterator( 878 ShadowTechnique shadowTechnique, const Light* light, 879 HardwareIndexBufferSharedPtr* indexBuffer, size_t* indexBufferUsedSize, 880 bool extrude, Real extrusionDistance, unsigned long flags) 881 { 882 // Calculate the object space light details 883 Vector4 lightPos = light->getAs4DVector(); 884 Affine3 world2Obj = mParentNode->_getFullTransform().inverse(); 885 lightPos = world2Obj * lightPos; 886 Matrix3 world2Obj3x3 = world2Obj.linear(); 887 extrusionDistance *= Math::Sqrt(std::min(std::min(world2Obj3x3.GetColumn(0).squaredLength(), world2Obj3x3.GetColumn(1).squaredLength()), world2Obj3x3.GetColumn(2).squaredLength())); 888 889 // per-LOD shadow lists & edge data 890 mLodBucketList[mCurrentLod]->updateShadowRenderables( 891 shadowTechnique, lightPos, indexBuffer, extrude, extrusionDistance, flags); 892 893 EdgeData* edgeList = mLodBucketList[mCurrentLod]->getEdgeList(); 894 ShadowRenderableList& shadowRendList = mLodBucketList[mCurrentLod]->getShadowRenderableList(); 895 896 // Calc triangle light facing 897 updateEdgeListLightFacing(edgeList, lightPos); 898 899 // Generate indexes and update renderables 900 generateShadowVolume(edgeList, *indexBuffer, *indexBufferUsedSize, 901 light, shadowRendList, flags); 902 903 904 return ShadowCaster::ShadowRenderableListIterator(shadowRendList.begin(), shadowRendList.end()); 905 906 } 907 //-------------------------------------------------------------------------- getEdgeList(void)908 EdgeData* StaticGeometry::Region::getEdgeList(void) 909 { 910 return mLodBucketList[mCurrentLod]->getEdgeList(); 911 } 912 //-------------------------------------------------------------------------- hasEdgeList(void)913 bool StaticGeometry::Region::hasEdgeList(void) 914 { 915 return getEdgeList() != 0; 916 } 917 //-------------------------------------------------------------------------- dump(std::ofstream & of) const918 void StaticGeometry::Region::dump(std::ofstream& of) const 919 { 920 of << "Region " << mRegionID << std::endl; 921 of << "--------------------------" << std::endl; 922 of << "Centre: " << mCentre << std::endl; 923 of << "Local AABB: " << mAABB << std::endl; 924 of << "Bounding radius: " << mBoundingRadius << std::endl; 925 of << "Number of LODs: " << mLodBucketList.size() << std::endl; 926 927 for (LODBucketList::const_iterator i = mLodBucketList.begin(); 928 i != mLodBucketList.end(); ++i) 929 { 930 (*i)->dump(of); 931 } 932 of << "--------------------------" << std::endl; 933 } 934 //-------------------------------------------------------------------------- 935 //-------------------------------------------------------------------------- LODShadowRenderable(LODBucket * parent,HardwareIndexBufferSharedPtr * indexBuffer,const VertexData * vertexData,bool createSeparateLightCap,bool isLightCap)936 StaticGeometry::LODBucket::LODShadowRenderable::LODShadowRenderable( 937 LODBucket* parent, HardwareIndexBufferSharedPtr* indexBuffer, 938 const VertexData* vertexData, bool createSeparateLightCap, 939 bool isLightCap) 940 : mParent(parent) 941 { 942 // Initialise render op 943 mRenderOp.indexData = OGRE_NEW IndexData(); 944 mRenderOp.indexData->indexBuffer = *indexBuffer; 945 mRenderOp.indexData->indexStart = 0; 946 // index start and count are sorted out later 947 948 // Create vertex data which just references position component (and 2 component) 949 mRenderOp.vertexData = OGRE_NEW VertexData(); 950 // Map in position data 951 mRenderOp.vertexData->vertexDeclaration->addElement(0,0,VET_FLOAT3, VES_POSITION); 952 ushort origPosBind = 953 vertexData->vertexDeclaration->findElementBySemantic(VES_POSITION)->getSource(); 954 mPositionBuffer = vertexData->vertexBufferBinding->getBuffer(origPosBind); 955 mRenderOp.vertexData->vertexBufferBinding->setBinding(0, mPositionBuffer); 956 // Map in w-coord buffer (if present) 957 if(vertexData->hardwareShadowVolWBuffer) 958 { 959 mRenderOp.vertexData->vertexDeclaration->addElement(1,0,VET_FLOAT1, VES_TEXTURE_COORDINATES, 0); 960 mWBuffer = vertexData->hardwareShadowVolWBuffer; 961 mRenderOp.vertexData->vertexBufferBinding->setBinding(1, mWBuffer); 962 } 963 // Use same vertex start as input 964 mRenderOp.vertexData->vertexStart = vertexData->vertexStart; 965 966 if (isLightCap) 967 { 968 // Use original vertex count, no extrusion 969 mRenderOp.vertexData->vertexCount = vertexData->vertexCount; 970 } 971 else 972 { 973 // Vertex count must take into account the doubling of the buffer, 974 // because second half of the buffer is the extruded copy 975 mRenderOp.vertexData->vertexCount = 976 vertexData->vertexCount * 2; 977 if (createSeparateLightCap) 978 { 979 // Create child light cap 980 mLightCap = OGRE_NEW LODShadowRenderable(parent, 981 indexBuffer, vertexData, false, true); 982 } 983 } 984 } 985 //-------------------------------------------------------------------------- ~LODShadowRenderable()986 StaticGeometry::LODBucket::LODShadowRenderable::~LODShadowRenderable() 987 { 988 OGRE_DELETE mRenderOp.indexData; 989 OGRE_DELETE mRenderOp.vertexData; 990 } 991 //-------------------------------------------------------------------------- getWorldTransforms(Matrix4 * xform) const992 void StaticGeometry::LODBucket::LODShadowRenderable::getWorldTransforms( 993 Matrix4* xform) const 994 { 995 // pretransformed 996 *xform = mParent->getParent()->_getParentNodeFullTransform(); 997 } 998 //----------------------------------------------------------------------- rebindIndexBuffer(const HardwareIndexBufferSharedPtr & indexBuffer)999 void StaticGeometry::LODBucket::LODShadowRenderable::rebindIndexBuffer(const HardwareIndexBufferSharedPtr& indexBuffer) 1000 { 1001 mRenderOp.indexData->indexBuffer = indexBuffer; 1002 if (mLightCap) mLightCap->rebindIndexBuffer(indexBuffer); 1003 } 1004 //-------------------------------------------------------------------------- 1005 //-------------------------------------------------------------------------- LODBucket(Region * parent,unsigned short lod,Real lodValue)1006 StaticGeometry::LODBucket::LODBucket(Region* parent, unsigned short lod, 1007 Real lodValue) 1008 : mParent(parent), mLod(lod), mLodValue(lodValue), mEdgeList(0) 1009 , mVertexProgramInUse(false) 1010 { 1011 } 1012 //-------------------------------------------------------------------------- ~LODBucket()1013 StaticGeometry::LODBucket::~LODBucket() 1014 { 1015 OGRE_DELETE mEdgeList; 1016 ShadowCaster::clearShadowRenderableList(mShadowRenderables); 1017 // delete 1018 for (MaterialBucketMap::iterator i = mMaterialBucketMap.begin(); 1019 i != mMaterialBucketMap.end(); ++i) 1020 { 1021 OGRE_DELETE i->second; 1022 } 1023 mMaterialBucketMap.clear(); 1024 for(QueuedGeometryList::iterator qi = mQueuedGeometryList.begin(); 1025 qi != mQueuedGeometryList.end(); ++qi) 1026 { 1027 OGRE_DELETE *qi; 1028 } 1029 mQueuedGeometryList.clear(); 1030 1031 // no need to delete queued meshes, these are managed in StaticGeometry 1032 } 1033 //-------------------------------------------------------------------------- assign(QueuedSubMesh * qmesh,ushort atLod)1034 void StaticGeometry::LODBucket::assign(QueuedSubMesh* qmesh, ushort atLod) 1035 { 1036 QueuedGeometry* q = OGRE_NEW QueuedGeometry(); 1037 mQueuedGeometryList.push_back(q); 1038 q->position = qmesh->position; 1039 q->orientation = qmesh->orientation; 1040 q->scale = qmesh->scale; 1041 if (qmesh->geometryLodList->size() > atLod) 1042 { 1043 // This submesh has enough lods, use the right one 1044 q->geometry = &(*qmesh->geometryLodList)[atLod]; 1045 } 1046 else 1047 { 1048 // Not enough lods, use the lowest one we have 1049 q->geometry = 1050 &(*qmesh->geometryLodList)[qmesh->geometryLodList->size() - 1]; 1051 } 1052 // Locate a material bucket 1053 MaterialBucket* mbucket = 0; 1054 MaterialBucketMap::iterator m = 1055 mMaterialBucketMap.find(qmesh->materialName); 1056 if (m != mMaterialBucketMap.end()) 1057 { 1058 mbucket = m->second; 1059 } 1060 else 1061 { 1062 mbucket = OGRE_NEW MaterialBucket(this, qmesh->materialName); 1063 mMaterialBucketMap[qmesh->materialName] = mbucket; 1064 } 1065 mbucket->assign(q); 1066 } 1067 //-------------------------------------------------------------------------- build(bool stencilShadows)1068 void StaticGeometry::LODBucket::build(bool stencilShadows) 1069 { 1070 1071 EdgeListBuilder eb; 1072 size_t vertexSet = 0; 1073 1074 // Just pass this on to child buckets 1075 for (MaterialBucketMap::iterator i = mMaterialBucketMap.begin(); 1076 i != mMaterialBucketMap.end(); ++i) 1077 { 1078 MaterialBucket* mat = i->second; 1079 1080 mat->build(stencilShadows); 1081 1082 if (stencilShadows) 1083 { 1084 MaterialBucket::GeometryIterator geomIt = 1085 mat->getGeometryIterator(); 1086 // Check if we have vertex programs here 1087 Technique* t = mat->getMaterial()->getBestTechnique(); 1088 if (t) 1089 { 1090 Pass* p = t->getPass(0); 1091 if (p) 1092 { 1093 if (p->hasVertexProgram()) 1094 { 1095 mVertexProgramInUse = true; 1096 } 1097 } 1098 } 1099 1100 while (geomIt.hasMoreElements()) 1101 { 1102 GeometryBucket* geom = geomIt.getNext(); 1103 1104 // Check we're dealing with 16-bit indexes here 1105 // Since stencil shadows can only deal with 16-bit 1106 // More than that and stencil is probably too CPU-heavy 1107 // in any case 1108 assert(geom->getIndexData()->indexBuffer->getType() 1109 == HardwareIndexBuffer::IT_16BIT && 1110 "Only 16-bit indexes allowed when using stencil shadows"); 1111 eb.addVertexData(geom->getVertexData()); 1112 eb.addIndexData(geom->getIndexData(), vertexSet++); 1113 } 1114 1115 } 1116 } 1117 1118 if (stencilShadows) 1119 { 1120 mEdgeList = eb.build(); 1121 } 1122 } 1123 //-------------------------------------------------------------------------- addRenderables(RenderQueue * queue,uint8 group,Real lodValue)1124 void StaticGeometry::LODBucket::addRenderables(RenderQueue* queue, 1125 uint8 group, Real lodValue) 1126 { 1127 // Just pass this on to child buckets 1128 MaterialBucketMap::iterator i, iend; 1129 iend = mMaterialBucketMap.end(); 1130 for (i = mMaterialBucketMap.begin(); i != iend; ++i) 1131 { 1132 i->second->addRenderables(queue, group, lodValue); 1133 } 1134 } 1135 //-------------------------------------------------------------------------- 1136 StaticGeometry::LODBucket::MaterialIterator getMaterialIterator(void)1137 StaticGeometry::LODBucket::getMaterialIterator(void) 1138 { 1139 return MaterialIterator( 1140 mMaterialBucketMap.begin(), mMaterialBucketMap.end()); 1141 } 1142 //-------------------------------------------------------------------------- dump(std::ofstream & of) const1143 void StaticGeometry::LODBucket::dump(std::ofstream& of) const 1144 { 1145 of << "LOD Bucket " << mLod << std::endl; 1146 of << "------------------" << std::endl; 1147 of << "LOD Value: " << mLodValue << std::endl; 1148 of << "Number of Materials: " << mMaterialBucketMap.size() << std::endl; 1149 for (MaterialBucketMap::const_iterator i = mMaterialBucketMap.begin(); 1150 i != mMaterialBucketMap.end(); ++i) 1151 { 1152 i->second->dump(of); 1153 } 1154 of << "------------------" << std::endl; 1155 1156 } 1157 //--------------------------------------------------------------------- visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)1158 void StaticGeometry::LODBucket::visitRenderables(Renderable::Visitor* visitor, 1159 bool debugRenderables) 1160 { 1161 for (MaterialBucketMap::const_iterator i = mMaterialBucketMap.begin(); 1162 i != mMaterialBucketMap.end(); ++i) 1163 { 1164 i->second->visitRenderables(visitor, debugRenderables); 1165 } 1166 1167 } 1168 //--------------------------------------------------------------------- updateShadowRenderables(ShadowTechnique shadowTechnique,const Vector4 & lightPos,HardwareIndexBufferSharedPtr * indexBuffer,bool extrude,Real extrusionDistance,unsigned long flags)1169 void StaticGeometry::LODBucket::updateShadowRenderables( 1170 ShadowTechnique shadowTechnique, const Vector4& lightPos, 1171 HardwareIndexBufferSharedPtr* indexBuffer, bool extrude, 1172 Real extrusionDistance, unsigned long flags /* = 0 */) 1173 { 1174 assert(indexBuffer && "Only external index buffers are supported right now"); 1175 assert((*indexBuffer)->getType() == HardwareIndexBuffer::IT_16BIT && 1176 "Only 16-bit indexes supported for now"); 1177 1178 // We need to search the edge list for silhouette edges 1179 if (!mEdgeList) 1180 { 1181 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 1182 "You enabled stencil shadows after the buid process!", 1183 "StaticGeometry::LODBucket::getShadowVolumeRenderableIterator"); 1184 } 1185 1186 // Init shadow renderable list if required 1187 bool init = mShadowRenderables.empty(); 1188 1189 EdgeData::EdgeGroupList::iterator egi; 1190 ShadowCaster::ShadowRenderableList::iterator si, siend; 1191 LODShadowRenderable* esr = 0; 1192 if (init) 1193 mShadowRenderables.resize(mEdgeList->edgeGroups.size()); 1194 1195 //bool updatedSharedGeomNormals = false; 1196 siend = mShadowRenderables.end(); 1197 egi = mEdgeList->edgeGroups.begin(); 1198 for (si = mShadowRenderables.begin(); si != siend; ++si, ++egi) 1199 { 1200 if (init) 1201 { 1202 // Create a new renderable, create a separate light cap if 1203 // we're using a vertex program (either for this model, or 1204 // for extruding the shadow volume) since otherwise we can 1205 // get depth-fighting on the light cap 1206 1207 *si = OGRE_NEW LODShadowRenderable(this, indexBuffer, 1208 egi->vertexData, mVertexProgramInUse || !extrude); 1209 } 1210 // Get shadow renderable 1211 esr = static_cast<LODShadowRenderable*>(*si); 1212 HardwareVertexBufferSharedPtr esrPositionBuffer = esr->getPositionBuffer(); 1213 // Extrude vertices in software if required 1214 if (extrude) 1215 { 1216 mParent->extrudeVertices(esrPositionBuffer, 1217 egi->vertexData->vertexCount, 1218 lightPos, extrusionDistance); 1219 1220 } 1221 1222 } 1223 1224 } 1225 //-------------------------------------------------------------------------- 1226 //-------------------------------------------------------------------------- MaterialBucket(LODBucket * parent,const String & materialName)1227 StaticGeometry::MaterialBucket::MaterialBucket(LODBucket* parent, 1228 const String& materialName) 1229 : mParent(parent) 1230 , mMaterialName(materialName) 1231 , mTechnique(0) 1232 { 1233 } 1234 //-------------------------------------------------------------------------- ~MaterialBucket()1235 StaticGeometry::MaterialBucket::~MaterialBucket() 1236 { 1237 // delete 1238 for (GeometryBucketList::iterator i = mGeometryBucketList.begin(); 1239 i != mGeometryBucketList.end(); ++i) 1240 { 1241 OGRE_DELETE *i; 1242 } 1243 mGeometryBucketList.clear(); 1244 1245 // no need to delete queued meshes, these are managed in StaticGeometry 1246 } 1247 //-------------------------------------------------------------------------- assign(QueuedGeometry * qgeom)1248 void StaticGeometry::MaterialBucket::assign(QueuedGeometry* qgeom) 1249 { 1250 // Look up any current geometry 1251 String formatString = getGeometryFormatString(qgeom->geometry); 1252 CurrentGeometryMap::iterator gi = mCurrentGeometryMap.find(formatString); 1253 bool newBucket = true; 1254 if (gi != mCurrentGeometryMap.end()) 1255 { 1256 // Found existing geometry, try to assign 1257 newBucket = !gi->second->assign(qgeom); 1258 // Note that this bucket will be replaced as the 'current' 1259 // for this format string below since it's out of space 1260 } 1261 // Do we need to create a new one? 1262 if (newBucket) 1263 { 1264 GeometryBucket* gbucket = OGRE_NEW GeometryBucket(this, formatString, 1265 qgeom->geometry->vertexData, qgeom->geometry->indexData); 1266 // Add to main list 1267 mGeometryBucketList.push_back(gbucket); 1268 // Also index in 'current' list 1269 mCurrentGeometryMap[formatString] = gbucket; 1270 if (!gbucket->assign(qgeom)) 1271 { 1272 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, 1273 "Somehow we couldn't fit the requested geometry even in a " 1274 "brand new GeometryBucket!! Must be a bug, please report.", 1275 "StaticGeometry::MaterialBucket::assign"); 1276 } 1277 } 1278 } 1279 //-------------------------------------------------------------------------- build(bool stencilShadows)1280 void StaticGeometry::MaterialBucket::build(bool stencilShadows) 1281 { 1282 mTechnique = 0; 1283 mMaterial = MaterialManager::getSingleton().getByName(mMaterialName); 1284 if (!mMaterial) 1285 { 1286 OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 1287 "Material '" + mMaterialName + "' not found.", 1288 "StaticGeometry::MaterialBucket::build"); 1289 } 1290 mMaterial->load(); 1291 // tell the geometry buckets to build 1292 for (GeometryBucketList::iterator i = mGeometryBucketList.begin(); 1293 i != mGeometryBucketList.end(); ++i) 1294 { 1295 (*i)->build(stencilShadows); 1296 } 1297 } 1298 //-------------------------------------------------------------------------- addRenderables(RenderQueue * queue,uint8 group,Real lodValue)1299 void StaticGeometry::MaterialBucket::addRenderables(RenderQueue* queue, 1300 uint8 group, Real lodValue) 1301 { 1302 // Get region 1303 Region *region = mParent->getParent(); 1304 1305 // Get material LOD strategy 1306 const LodStrategy *materialLodStrategy = mMaterial->getLodStrategy(); 1307 1308 // If material strategy doesn't match, recompute LOD value with correct strategy 1309 if (materialLodStrategy != region->mLodStrategy) 1310 lodValue = materialLodStrategy->getValue(region, region->mCamera); 1311 1312 // Determine the current material technique 1313 mTechnique = mMaterial->getBestTechnique( 1314 mMaterial->getLodIndex(lodValue)); 1315 GeometryBucketList::iterator i, iend; 1316 iend = mGeometryBucketList.end(); 1317 for (i = mGeometryBucketList.begin(); i != iend; ++i) 1318 { 1319 queue->addRenderable(*i, group); 1320 } 1321 1322 } 1323 //-------------------------------------------------------------------------- getGeometryFormatString(SubMeshLodGeometryLink * geom)1324 String StaticGeometry::MaterialBucket::getGeometryFormatString( 1325 SubMeshLodGeometryLink* geom) 1326 { 1327 // Formulate an identifying string for the geometry format 1328 // Must take into account the vertex declaration and the index type 1329 // Format is (all lines separated by '|'): 1330 // Index type 1331 // Vertex element (repeating) 1332 // source 1333 // semantic 1334 // type 1335 StringStream str; 1336 1337 str << geom->indexData->indexBuffer->getType() << "|"; 1338 const VertexDeclaration::VertexElementList& elemList = 1339 geom->vertexData->vertexDeclaration->getElements(); 1340 VertexDeclaration::VertexElementList::const_iterator ei, eiend; 1341 eiend = elemList.end(); 1342 for (ei = elemList.begin(); ei != eiend; ++ei) 1343 { 1344 const VertexElement& elem = *ei; 1345 str << elem.getSource() << "|"; 1346 str << elem.getSource() << "|"; 1347 str << elem.getSemantic() << "|"; 1348 str << elem.getType() << "|"; 1349 } 1350 1351 return str.str(); 1352 1353 } 1354 //-------------------------------------------------------------------------- 1355 StaticGeometry::MaterialBucket::GeometryIterator getGeometryIterator(void)1356 StaticGeometry::MaterialBucket::getGeometryIterator(void) 1357 { 1358 return GeometryIterator( 1359 mGeometryBucketList.begin(), mGeometryBucketList.end()); 1360 } 1361 //-------------------------------------------------------------------------- dump(std::ofstream & of) const1362 void StaticGeometry::MaterialBucket::dump(std::ofstream& of) const 1363 { 1364 of << "Material Bucket " << mMaterialName << std::endl; 1365 of << "--------------------------------------------------" << std::endl; 1366 of << "Geometry buckets: " << mGeometryBucketList.size() << std::endl; 1367 for (GeometryBucketList::const_iterator i = mGeometryBucketList.begin(); 1368 i != mGeometryBucketList.end(); ++i) 1369 { 1370 (*i)->dump(of); 1371 } 1372 of << "--------------------------------------------------" << std::endl; 1373 1374 } 1375 //--------------------------------------------------------------------- visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)1376 void StaticGeometry::MaterialBucket::visitRenderables(Renderable::Visitor* visitor, 1377 bool debugRenderables) 1378 { 1379 for (GeometryBucketList::const_iterator i = mGeometryBucketList.begin(); 1380 i != mGeometryBucketList.end(); ++i) 1381 { 1382 visitor->visit(*i, mParent->getLod(), false); 1383 } 1384 1385 } 1386 //-------------------------------------------------------------------------- 1387 //-------------------------------------------------------------------------- GeometryBucket(MaterialBucket * parent,const String & formatString,const VertexData * vData,const IndexData * iData)1388 StaticGeometry::GeometryBucket::GeometryBucket(MaterialBucket* parent, 1389 const String& formatString, const VertexData* vData, 1390 const IndexData* iData) 1391 : Renderable(), mParent(parent), mFormatString(formatString) 1392 { 1393 // Clone the structure from the example 1394 mVertexData = vData->clone(false); 1395 mIndexData = iData->clone(false); 1396 mVertexData->vertexCount = 0; 1397 mVertexData->vertexStart = 0; 1398 mIndexData->indexCount = 0; 1399 mIndexData->indexStart = 0; 1400 mIndexType = iData->indexBuffer->getType(); 1401 // Derive the max vertices 1402 if (mIndexType == HardwareIndexBuffer::IT_32BIT) 1403 { 1404 mMaxVertexIndex = 0xFFFFFFFF; 1405 } 1406 else 1407 { 1408 mMaxVertexIndex = 0xFFFF; 1409 } 1410 1411 // Check to see if we have blend indices / blend weights 1412 // remove them if so, they can try to blend non-existent bones! 1413 const VertexElement* blendIndices = 1414 mVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_INDICES); 1415 const VertexElement* blendWeights = 1416 mVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_WEIGHTS); 1417 if (blendIndices && blendWeights) 1418 { 1419 assert(blendIndices->getSource() == blendWeights->getSource() 1420 && "Blend indices and weights should be in the same buffer"); 1421 // Get the source 1422 ushort source = blendIndices->getSource(); 1423 assert(blendIndices->getSize() + blendWeights->getSize() == 1424 mVertexData->vertexBufferBinding->getBuffer(source)->getVertexSize() 1425 && "Blend indices and blend buffers should have buffer to themselves!"); 1426 // Unset the buffer 1427 mVertexData->vertexBufferBinding->unsetBinding(source); 1428 // Remove the elements 1429 mVertexData->vertexDeclaration->removeElement(VES_BLEND_INDICES); 1430 mVertexData->vertexDeclaration->removeElement(VES_BLEND_WEIGHTS); 1431 // Close gaps in bindings for effective and safely 1432 mVertexData->closeGapsInBindings(); 1433 } 1434 1435 1436 } 1437 //-------------------------------------------------------------------------- ~GeometryBucket()1438 StaticGeometry::GeometryBucket::~GeometryBucket() 1439 { 1440 OGRE_DELETE mVertexData; 1441 OGRE_DELETE mIndexData; 1442 } 1443 //-------------------------------------------------------------------------- getMaterial(void) const1444 const MaterialPtr& StaticGeometry::GeometryBucket::getMaterial(void) const 1445 { 1446 return mParent->getMaterial(); 1447 } 1448 //-------------------------------------------------------------------------- getTechnique(void) const1449 Technique* StaticGeometry::GeometryBucket::getTechnique(void) const 1450 { 1451 return mParent->getCurrentTechnique(); 1452 } 1453 //-------------------------------------------------------------------------- getRenderOperation(RenderOperation & op)1454 void StaticGeometry::GeometryBucket::getRenderOperation(RenderOperation& op) 1455 { 1456 op.indexData = mIndexData; 1457 op.operationType = RenderOperation::OT_TRIANGLE_LIST; 1458 op.srcRenderable = this; 1459 op.useIndexes = true; 1460 op.vertexData = mVertexData; 1461 } 1462 //-------------------------------------------------------------------------- getWorldTransforms(Matrix4 * xform) const1463 void StaticGeometry::GeometryBucket::getWorldTransforms(Matrix4* xform) const 1464 { 1465 // Should be the identity transform, but lets allow transformation of the 1466 // nodes the regions are attached to for kicks 1467 *xform = mParent->getParent()->getParent()->_getParentNodeFullTransform(); 1468 } 1469 //-------------------------------------------------------------------------- getSquaredViewDepth(const Camera * cam) const1470 Real StaticGeometry::GeometryBucket::getSquaredViewDepth(const Camera* cam) const 1471 { 1472 const Region *region = mParent->getParent()->getParent(); 1473 if (cam == region->mCamera) 1474 return region->mSquaredViewDepth; 1475 else 1476 return region->getParentNode()->getSquaredViewDepth(cam->getLodCamera()); 1477 } 1478 //-------------------------------------------------------------------------- getLights(void) const1479 const LightList& StaticGeometry::GeometryBucket::getLights(void) const 1480 { 1481 return mParent->getParent()->getParent()->queryLights(); 1482 } 1483 //-------------------------------------------------------------------------- getCastsShadows(void) const1484 bool StaticGeometry::GeometryBucket::getCastsShadows(void) const 1485 { 1486 return mParent->getParent()->getParent()->getCastShadows(); 1487 } 1488 //-------------------------------------------------------------------------- assign(QueuedGeometry * qgeom)1489 bool StaticGeometry::GeometryBucket::assign(QueuedGeometry* qgeom) 1490 { 1491 // Do we have enough space? 1492 // -2 first to avoid overflow (-1 to adjust count to index, -1 to ensure 1493 // no overflow at 32 bits and use >= instead of >) 1494 if ((mVertexData->vertexCount - 2 + qgeom->geometry->vertexData->vertexCount) 1495 >= mMaxVertexIndex) 1496 { 1497 return false; 1498 } 1499 1500 mQueuedGeometry.push_back(qgeom); 1501 mVertexData->vertexCount += qgeom->geometry->vertexData->vertexCount; 1502 mIndexData->indexCount += qgeom->geometry->indexData->indexCount; 1503 1504 return true; 1505 } 1506 //-------------------------------------------------------------------------- build(bool stencilShadows)1507 void StaticGeometry::GeometryBucket::build(bool stencilShadows) 1508 { 1509 // Ok, here's where we transfer the vertices and indexes to the shared 1510 // buffers 1511 // Shortcuts 1512 VertexDeclaration* dcl = mVertexData->vertexDeclaration; 1513 VertexBufferBinding* binds = mVertexData->vertexBufferBinding; 1514 1515 // create index buffer, and lock 1516 mIndexData->indexBuffer = HardwareBufferManager::getSingleton() 1517 .createIndexBuffer(mIndexType, mIndexData->indexCount, 1518 HardwareBuffer::HBU_STATIC_WRITE_ONLY); 1519 HardwareBufferLockGuard dstIndexLock(mIndexData->indexBuffer, HardwareBuffer::HBL_DISCARD); 1520 uint32* p32Dest = static_cast<uint32*>(dstIndexLock.pData); 1521 uint16* p16Dest = static_cast<uint16*>(dstIndexLock.pData); 1522 // create all vertex buffers, and lock 1523 ushort b; 1524 ushort posBufferIdx = dcl->findElementBySemantic(VES_POSITION)->getSource(); 1525 1526 std::vector<uchar*> destBufferLocks; 1527 std::vector<VertexDeclaration::VertexElementList> bufferElements; 1528 for (b = 0; b < binds->getBufferCount(); ++b) 1529 { 1530 size_t vertexCount = mVertexData->vertexCount; 1531 // Need to double the vertex count for the position buffer 1532 // if we're doing stencil shadows 1533 if (stencilShadows && b == posBufferIdx) 1534 { 1535 vertexCount = vertexCount * 2; 1536 assert(vertexCount <= mMaxVertexIndex && 1537 "Index range exceeded when using stencil shadows, consider " 1538 "reducing your region size or reducing poly count"); 1539 } 1540 HardwareVertexBufferSharedPtr vbuf = 1541 HardwareBufferManager::getSingleton().createVertexBuffer( 1542 dcl->getVertexSize(b), 1543 vertexCount, 1544 HardwareBuffer::HBU_STATIC_WRITE_ONLY); 1545 binds->setBinding(b, vbuf); 1546 uchar* pLock = static_cast<uchar*>( 1547 vbuf->lock(HardwareBuffer::HBL_DISCARD)); 1548 destBufferLocks.push_back(pLock); 1549 // Pre-cache vertex elements per buffer 1550 bufferElements.push_back(dcl->findElementsBySource(b)); 1551 } 1552 1553 1554 // Iterate over the geometry items 1555 size_t indexOffset = 0; 1556 QueuedGeometryList::iterator gi, giend; 1557 giend = mQueuedGeometry.end(); 1558 Vector3 regionCentre = mParent->getParent()->getParent()->getCentre(); 1559 for (gi = mQueuedGeometry.begin(); gi != giend; ++gi) 1560 { 1561 QueuedGeometry* geom = *gi; 1562 // Copy indexes across with offset 1563 IndexData* srcIdxData = geom->geometry->indexData; 1564 HardwareBufferLockGuard srcIdxLock(srcIdxData->indexBuffer, 1565 srcIdxData->indexStart * srcIdxData->indexBuffer->getIndexSize(), 1566 srcIdxData->indexCount * srcIdxData->indexBuffer->getIndexSize(), 1567 HardwareBuffer::HBL_READ_ONLY); 1568 if (mIndexType == HardwareIndexBuffer::IT_32BIT) 1569 { 1570 uint32* pSrc = static_cast<uint32*>(srcIdxLock.pData); 1571 copyIndexes(pSrc, p32Dest, srcIdxData->indexCount, indexOffset); 1572 p32Dest += srcIdxData->indexCount; 1573 } 1574 else 1575 { 1576 // Lock source indexes 1577 uint16* pSrc = static_cast<uint16*>(srcIdxLock.pData); 1578 copyIndexes(pSrc, p16Dest, srcIdxData->indexCount, indexOffset); 1579 p16Dest += srcIdxData->indexCount; 1580 } 1581 srcIdxLock.unlock(); 1582 1583 // Now deal with vertex buffers 1584 // we can rely on buffer counts / formats being the same 1585 VertexData* srcVData = geom->geometry->vertexData; 1586 VertexBufferBinding* srcBinds = srcVData->vertexBufferBinding; 1587 for (b = 0; b < binds->getBufferCount(); ++b) 1588 { 1589 // lock source 1590 HardwareVertexBufferSharedPtr srcBuf = srcBinds->getBuffer(b); 1591 HardwareBufferLockGuard srcBufLock(srcBuf, HardwareBuffer::HBL_READ_ONLY); 1592 uchar* pSrcBase = static_cast<uchar*>(srcBufLock.pData); 1593 // Get buffer lock pointer, we'll update this later 1594 uchar* pDstBase = destBufferLocks[b]; 1595 size_t bufInc = srcBuf->getVertexSize(); 1596 1597 // Iterate over vertices 1598 float *pSrcReal, *pDstReal; 1599 Vector3 tmp; 1600 for (size_t v = 0; v < srcVData->vertexCount; ++v) 1601 { 1602 // Iterate over vertex elements 1603 VertexDeclaration::VertexElementList& elems = 1604 bufferElements[b]; 1605 VertexDeclaration::VertexElementList::iterator ei; 1606 for (ei = elems.begin(); ei != elems.end(); ++ei) 1607 { 1608 VertexElement& elem = *ei; 1609 elem.baseVertexPointerToElement(pSrcBase, &pSrcReal); 1610 elem.baseVertexPointerToElement(pDstBase, &pDstReal); 1611 switch (elem.getSemantic()) 1612 { 1613 case VES_POSITION: 1614 tmp.x = *pSrcReal++; 1615 tmp.y = *pSrcReal++; 1616 tmp.z = *pSrcReal++; 1617 // transform 1618 tmp = (geom->orientation * (tmp * geom->scale)) + 1619 geom->position; 1620 // Adjust for region centre 1621 tmp -= regionCentre; 1622 *pDstReal++ = tmp.x; 1623 *pDstReal++ = tmp.y; 1624 *pDstReal++ = tmp.z; 1625 break; 1626 case VES_NORMAL: 1627 case VES_TANGENT: 1628 case VES_BINORMAL: 1629 tmp.x = *pSrcReal++; 1630 tmp.y = *pSrcReal++; 1631 tmp.z = *pSrcReal++; 1632 // scale (invert) 1633 tmp = tmp / geom->scale; 1634 tmp.normalise(); 1635 // rotation 1636 tmp = geom->orientation * tmp; 1637 *pDstReal++ = tmp.x; 1638 *pDstReal++ = tmp.y; 1639 *pDstReal++ = tmp.z; 1640 // copy parity for tangent. 1641 if (elem.getType() == Ogre::VET_FLOAT4) 1642 *pDstReal = *pSrcReal; 1643 break; 1644 default: 1645 // just raw copy 1646 memcpy(pDstReal, pSrcReal, 1647 VertexElement::getTypeSize(elem.getType())); 1648 break; 1649 }; 1650 1651 } 1652 1653 // Increment both pointers 1654 pDstBase += bufInc; 1655 pSrcBase += bufInc; 1656 1657 } 1658 1659 // Update pointer 1660 destBufferLocks[b] = pDstBase; 1661 } 1662 1663 indexOffset += geom->geometry->vertexData->vertexCount; 1664 } 1665 1666 // Unlock everything 1667 dstIndexLock.unlock(); 1668 for (b = 0; b < binds->getBufferCount(); ++b) 1669 { 1670 binds->getBuffer(b)->unlock(); 1671 } 1672 1673 // If we're dealing with stencil shadows, copy the position data from 1674 // the early half of the buffer to the latter part 1675 if (stencilShadows) 1676 { 1677 HardwareVertexBufferSharedPtr buf = binds->getBuffer(posBufferIdx); 1678 HardwareBufferLockGuard bufLock(buf, HardwareBuffer::HBL_NORMAL); 1679 void* pSrc = bufLock.pData; 1680 // Point dest at second half (remember vertexcount is original count) 1681 void* pDest = static_cast<uchar*>(pSrc) + 1682 buf->getVertexSize() * mVertexData->vertexCount; 1683 memcpy(pDest, pSrc, buf->getVertexSize() * mVertexData->vertexCount); 1684 bufLock.unlock(); 1685 1686 // Also set up hardware W buffer if appropriate 1687 RenderSystem* rend = Root::getSingleton().getRenderSystem(); 1688 if (rend && rend->getCapabilities()->hasCapability(RSC_VERTEX_PROGRAM)) 1689 { 1690 buf = HardwareBufferManager::getSingleton().createVertexBuffer( 1691 sizeof(float), mVertexData->vertexCount * 2, 1692 HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); 1693 // Fill the first half with 1.0, second half with 0.0 1694 bufLock.lock(buf, HardwareBuffer::HBL_DISCARD); 1695 float *pW = static_cast<float*>(bufLock.pData); 1696 size_t v; 1697 for (v = 0; v < mVertexData->vertexCount; ++v) 1698 { 1699 *pW++ = 1.0f; 1700 } 1701 for (v = 0; v < mVertexData->vertexCount; ++v) 1702 { 1703 *pW++ = 0.0f; 1704 } 1705 bufLock.unlock(); 1706 mVertexData->hardwareShadowVolWBuffer = buf; 1707 } 1708 } 1709 1710 } 1711 //-------------------------------------------------------------------------- dump(std::ofstream & of) const1712 void StaticGeometry::GeometryBucket::dump(std::ofstream& of) const 1713 { 1714 of << "Geometry Bucket" << std::endl; 1715 of << "---------------" << std::endl; 1716 of << "Format string: " << mFormatString << std::endl; 1717 of << "Geometry items: " << mQueuedGeometry.size() << std::endl; 1718 of << "Vertex count: " << mVertexData->vertexCount << std::endl; 1719 of << "Index count: " << mIndexData->indexCount << std::endl; 1720 of << "---------------" << std::endl; 1721 1722 } 1723 //-------------------------------------------------------------------------- 1724 1725 } 1726 1727