/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2005 The OGRE Team Also see acknowledgements in Readme.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. ----------------------------------------------------------------------------- */ #include "OgreStableHeaders.h" #include "OgreStaticGeometry.h" #include "OgreEntity.h" #include "OgreSubEntity.h" #include "OgreSceneNode.h" #include "OgreException.h" #include "OgreMesh.h" #include "OgreSubMesh.h" #include "OgreLogManager.h" #include "OgreSceneManager.h" #include "OgreCamera.h" #include "OgreMaterialManager.h" #include "OgreRoot.h" #include "OgreRenderSystem.h" #include "OgreEdgeListBuilder.h" namespace Ogre { #define REGION_RANGE 1024 #define REGION_HALF_RANGE 512 #define REGION_MAX_INDEX 511 #define REGION_MIN_INDEX -512 //-------------------------------------------------------------------------- StaticGeometry::StaticGeometry(SceneManager* owner, const String& name): mOwner(owner), mName(name), mBuilt(false), mUpperDistance(0.0f), mSquaredUpperDistance(0.0f), mCastShadows(false), mRegionDimensions(Vector3(1000,1000,1000)), mHalfRegionDimensions(Vector3(500,500,500)), mOrigin(Vector3(0,0,0)), mVisible(true), mRenderQueueID(RENDER_QUEUE_MAIN), mRenderQueueIDSet(false) { } //-------------------------------------------------------------------------- StaticGeometry::~StaticGeometry() { reset(); } //-------------------------------------------------------------------------- StaticGeometry::Region* StaticGeometry::getRegion(const AxisAlignedBox& bounds, bool autoCreate) { if (bounds.isNull()) return 0; // Get the region which has the largest overlapping volume const Vector3 min = bounds.getMinimum(); const Vector3 max = bounds.getMaximum(); // Get the min and max region indexes ushort minx, miny, minz; ushort maxx, maxy, maxz; getRegionIndexes(min, minx, miny, minz); getRegionIndexes(max, maxx, maxy, maxz); Real maxVolume = 0.0f; ushort finalx, finaly, finalz; for (ushort x = minx; x <= maxx; ++x) { for (ushort y = miny; y <= maxy; ++y) { for (ushort z = minz; z <= maxz; ++z) { Real vol = getVolumeIntersection(bounds, x, y, z); if (vol > maxVolume) { maxVolume = vol; finalx = x; finaly = y; finalz = z; } } } } assert(maxVolume > 0.0f && "Static geometry: Problem determining closest volume match!"); return getRegion(finalx, finaly, finalz, autoCreate); } //-------------------------------------------------------------------------- Real StaticGeometry::getVolumeIntersection(const AxisAlignedBox& box, ushort x, ushort y, ushort z) { // Get bounds of indexed region AxisAlignedBox regionBounds = getRegionBounds(x, y, z); AxisAlignedBox intersectBox = regionBounds.intersection(box); // return a 'volume' which ignores zero dimensions // since we only use this for relative comparisons of the same bounds // this will still be internally consistent Vector3 boxdiff = box.getMaximum() - box.getMinimum(); Vector3 intersectDiff = intersectBox.getMaximum() - intersectBox.getMinimum(); return (boxdiff.x == 0 ? 1 : intersectDiff.x) * (boxdiff.y == 0 ? 1 : intersectDiff.y) * (boxdiff.z == 0 ? 1 : intersectDiff.z); } //-------------------------------------------------------------------------- AxisAlignedBox StaticGeometry::getRegionBounds(ushort x, ushort y, ushort z) { Vector3 min( ((Real)x - REGION_HALF_RANGE) * mRegionDimensions.x + mOrigin.x, ((Real)y - REGION_HALF_RANGE) * mRegionDimensions.y + mOrigin.y, ((Real)z - REGION_HALF_RANGE) * mRegionDimensions.z + mOrigin.z ); Vector3 max = min + mRegionDimensions; return AxisAlignedBox(min, max); } //-------------------------------------------------------------------------- Vector3 StaticGeometry::getRegionCentre(ushort x, ushort y, ushort z) { return Vector3( ((Real)x - REGION_HALF_RANGE) * mRegionDimensions.x + mOrigin.x + mHalfRegionDimensions.x, ((Real)y - REGION_HALF_RANGE) * mRegionDimensions.y + mOrigin.y + mHalfRegionDimensions.y, ((Real)z - REGION_HALF_RANGE) * mRegionDimensions.z + mOrigin.z + mHalfRegionDimensions.z ); } //-------------------------------------------------------------------------- StaticGeometry::Region* StaticGeometry::getRegion( ushort x, ushort y, ushort z, bool autoCreate) { uint32 index = packIndex(x, y, z); Region* ret = getRegion(index); if (!ret && autoCreate) { // Make a name StringUtil::StrStreamType str; str << mName << ":" << index; // Calculate the region centre Vector3 centre = getRegionCentre(x, y, z); ret = new Region(this, str.str(), mOwner, index, centre); mOwner->injectMovableObject(ret); ret->setVisible(mVisible); ret->setCastShadows(mCastShadows); if (mRenderQueueIDSet) { ret->setRenderQueueGroup(mRenderQueueID); } mRegionMap[index] = ret; } return ret; } //-------------------------------------------------------------------------- StaticGeometry::Region* StaticGeometry::getRegion(uint32 index) { RegionMap::iterator i = mRegionMap.find(index); if (i != mRegionMap.end()) { return i->second; } else { return 0; } } //-------------------------------------------------------------------------- void StaticGeometry::getRegionIndexes(const Vector3& point, ushort& x, ushort& y, ushort& z) { // Scale the point into multiples of region and adjust for origin Vector3 scaledPoint = (point - mOrigin) / mRegionDimensions; // Round down to 'bottom left' point which represents the cell index int ix = Math::IFloor(scaledPoint.x); int iy = Math::IFloor(scaledPoint.y); int iz = Math::IFloor(scaledPoint.z); // Check bounds if (ix < REGION_MIN_INDEX || ix > REGION_MAX_INDEX || iy < REGION_MIN_INDEX || iy > REGION_MAX_INDEX || iz < REGION_MIN_INDEX || iz > REGION_MAX_INDEX) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Point out of bounds", "StaticGeometry::getRegionIndexes"); } // Adjust for the fact that we use unsigned values for simplicity // (requires less faffing about for negatives give 10-bit packing x = static_cast(ix + REGION_HALF_RANGE); y = static_cast(iy + REGION_HALF_RANGE); z = static_cast(iz + REGION_HALF_RANGE); } //-------------------------------------------------------------------------- uint32 StaticGeometry::packIndex(ushort x, ushort y, ushort z) { return x + (y << 10) + (z << 20); } //-------------------------------------------------------------------------- StaticGeometry::Region* StaticGeometry::getRegion(const Vector3& point, bool autoCreate) { ushort x, y, z; getRegionIndexes(point, x, y, z); return getRegion(x, y, z, autoCreate); } //-------------------------------------------------------------------------- AxisAlignedBox StaticGeometry::calculateBounds(VertexData* vertexData, const Vector3& position, const Quaternion& orientation, const Vector3& scale) { const VertexElement* posElem = vertexData->vertexDeclaration->findElementBySemantic( VES_POSITION); HardwareVertexBufferSharedPtr vbuf = vertexData->vertexBufferBinding->getBuffer(posElem->getSource()); unsigned char* vertex = static_cast( vbuf->lock(HardwareBuffer::HBL_READ_ONLY)); float* pFloat; Vector3 min, max; bool first = true; for(size_t j = 0; j < vertexData->vertexCount; ++j, vertex += vbuf->getVertexSize()) { posElem->baseVertexPointerToElement(vertex, &pFloat); Vector3 pt; pt.x = (*pFloat++); pt.y = (*pFloat++); pt.z = (*pFloat++); // Transform to world (scale, rotate, translate) pt = (orientation * (pt * scale)) + position; if (first) { min = max = pt; first = false; } else { min.makeFloor(pt); max.makeCeil(pt); } } vbuf->unlock(); return AxisAlignedBox(min, max); } //-------------------------------------------------------------------------- void StaticGeometry::addEntity(Entity* ent, const Vector3& position, const Quaternion& orientation, const Vector3& scale) { const MeshPtr& msh = ent->getMesh(); // Validate if (msh->isLodManual()) { LogManager::getSingleton().logMessage( "WARNING (StaticGeometry): Manual LOD is not supported. " "Using only highest LOD level for mesh " + msh->getName()); } AxisAlignedBox sharedWorldBounds; // queue this entities submeshes and choice of material // also build the lists of geometry to be used for the source of lods for (uint i = 0; i < ent->getNumSubEntities(); ++i) { SubEntity* se = ent->getSubEntity(i); QueuedSubMesh* q = new QueuedSubMesh(); // Get the geometry for this SubMesh q->submesh = se->getSubMesh(); q->geometryLodList = determineGeometry(q->submesh); q->materialName = se->getMaterialName(); q->orientation = orientation; q->position = position; q->scale = scale; // Determine the bounds based on the highest LOD q->worldBounds = calculateBounds( (*q->geometryLodList)[0].vertexData, position, orientation, scale); mQueuedSubMeshes.push_back(q); } } //-------------------------------------------------------------------------- StaticGeometry::SubMeshLodGeometryLinkList* StaticGeometry::determineGeometry(SubMesh* sm) { // First, determine if we've already seen this submesh before SubMeshGeometryLookup::iterator i = mSubMeshGeometryLookup.find(sm); if (i != mSubMeshGeometryLookup.end()) { return i->second; } // Otherwise, we have to create a new one SubMeshLodGeometryLinkList* lodList = new SubMeshLodGeometryLinkList(); mSubMeshGeometryLookup[sm] = lodList; ushort numLods = sm->parent->isLodManual() ? 1 : sm->parent->getNumLodLevels(); lodList->resize(numLods); for (ushort lod = 0; lod < numLods; ++lod) { SubMeshLodGeometryLink& geomLink = (*lodList)[lod]; IndexData *lodIndexData; if (lod == 0) { lodIndexData = sm->indexData; } else { lodIndexData = sm->mLodFaceList[lod - 1]; } // Can use the original mesh geometry? if (sm->useSharedVertices) { if (sm->parent->getNumSubMeshes() == 1) { // Ok, this is actually our own anyway geomLink.vertexData = sm->parent->sharedVertexData; geomLink.indexData = lodIndexData; } else { // We have to split it splitGeometry(sm->parent->sharedVertexData, lodIndexData, &geomLink); } } else { if (lod == 0) { // Ok, we can use the existing geometry; should be in full // use by just this SubMesh geomLink.vertexData = sm->vertexData; geomLink.indexData = sm->indexData; } else { // We have to split it splitGeometry(sm->vertexData, lodIndexData, &geomLink); } } assert (geomLink.vertexData->vertexStart == 0 && "Cannot use vertexStart > 0 on indexed geometry due to " "rendersystem incompatibilities - see the docs!"); } return lodList; } //-------------------------------------------------------------------------- void StaticGeometry::splitGeometry(VertexData* vd, IndexData* id, StaticGeometry::SubMeshLodGeometryLink* targetGeomLink) { // Firstly we need to scan to see how many vertices are being used // and while we're at it, build the remap we can use later bool use32bitIndexes = id->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT; uint16 *p16; uint32 *p32; IndexRemap indexRemap; if (use32bitIndexes) { p32 = static_cast(id->indexBuffer->lock( id->indexStart, id->indexCount, HardwareBuffer::HBL_READ_ONLY)); buildIndexRemap(p32, id->indexCount, indexRemap); id->indexBuffer->unlock(); } else { p16 = static_cast(id->indexBuffer->lock( id->indexStart, id->indexCount, HardwareBuffer::HBL_READ_ONLY)); buildIndexRemap(p16, id->indexCount, indexRemap); id->indexBuffer->unlock(); } if (indexRemap.size() == vd->vertexCount) { // ha, complete usage after all targetGeomLink->vertexData = vd; targetGeomLink->indexData = id; return; } // Create the new vertex data records targetGeomLink->vertexData = vd->clone(false); // Convenience VertexData* newvd = targetGeomLink->vertexData; //IndexData* newid = targetGeomLink->indexData; // Update the vertex count newvd->vertexCount = indexRemap.size(); size_t numvbufs = vd->vertexBufferBinding->getBufferCount(); // Copy buffers from old to new for (unsigned short b = 0; b < numvbufs; ++b) { // Lock old buffer HardwareVertexBufferSharedPtr oldBuf = vd->vertexBufferBinding->getBuffer(b); // Create new buffer HardwareVertexBufferSharedPtr newBuf = HardwareBufferManager::getSingleton().createVertexBuffer( oldBuf->getVertexSize(), indexRemap.size(), HardwareBuffer::HBU_STATIC); // rebind newvd->vertexBufferBinding->setBinding(b, newBuf); // Copy all the elements of the buffer across, by iterating over // the IndexRemap which describes how to move the old vertices // to the new ones. By nature of the map the remap is in order of // indexes in the old buffer, but note that we're not guaranteed to // address every vertex (which is kinda why we're here) uchar* pSrcBase = static_cast( oldBuf->lock(HardwareBuffer::HBL_READ_ONLY)); uchar* pDstBase = static_cast( newBuf->lock(HardwareBuffer::HBL_DISCARD)); size_t vertexSize = oldBuf->getVertexSize(); // Buffers should be the same size assert (vertexSize == newBuf->getVertexSize()); for (IndexRemap::iterator r = indexRemap.begin(); r != indexRemap.end(); ++r) { assert (r->first < oldBuf->getNumVertices()); assert (r->second < newBuf->getNumVertices()); uchar* pSrc = pSrcBase + r->first * vertexSize; uchar* pDst = pDstBase + r->second * vertexSize; memcpy(pDst, pSrc, vertexSize); } // unlock oldBuf->unlock(); newBuf->unlock(); } // Now create a new index buffer HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().createIndexBuffer( id->indexBuffer->getType(), id->indexCount, HardwareBuffer::HBU_STATIC); if (use32bitIndexes) { uint32 *pSrc32, *pDst32; pSrc32 = static_cast(id->indexBuffer->lock( id->indexStart, id->indexCount, HardwareBuffer::HBL_READ_ONLY)); pDst32 = static_cast(ibuf->lock( HardwareBuffer::HBL_DISCARD)); remapIndexes(pSrc32, pDst32, indexRemap, id->indexCount); id->indexBuffer->unlock(); ibuf->unlock(); } else { uint16 *pSrc16, *pDst16; pSrc16 = static_cast(id->indexBuffer->lock( id->indexStart, id->indexCount, HardwareBuffer::HBL_READ_ONLY)); pDst16 = static_cast(ibuf->lock( HardwareBuffer::HBL_DISCARD)); remapIndexes(pSrc16, pDst16, indexRemap, id->indexCount); id->indexBuffer->unlock(); ibuf->unlock(); } targetGeomLink->indexData = new IndexData(); targetGeomLink->indexData->indexStart = 0; targetGeomLink->indexData->indexCount = id->indexCount; targetGeomLink->indexData->indexBuffer = ibuf; // Store optimised geometry for deallocation later OptimisedSubMeshGeometry *optGeom = new OptimisedSubMeshGeometry(); optGeom->indexData = targetGeomLink->indexData; optGeom->vertexData = targetGeomLink->vertexData; mOptimisedSubMeshGeometryList.push_back(optGeom); } //-------------------------------------------------------------------------- void StaticGeometry::addSceneNode(const SceneNode* node) { SceneNode::ConstObjectIterator obji = node->getAttachedObjectIterator(); while (obji.hasMoreElements()) { MovableObject* mobj = obji.getNext(); if (mobj->getMovableType() == "Entity") { addEntity(static_cast(mobj), node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); } } // Iterate through all the child-nodes SceneNode::ConstChildNodeIterator nodei = node->getChildIterator(); while (nodei.hasMoreElements()) { const SceneNode* node = static_cast(nodei.getNext()); // Add this subnode and its children... addSceneNode( node ); } } //-------------------------------------------------------------------------- void StaticGeometry::build(void) { // Make sure there's nothing from previous builds destroy(); // Firstly allocate meshes to regions for (QueuedSubMeshList::iterator qi = mQueuedSubMeshes.begin(); qi != mQueuedSubMeshes.end(); ++qi) { QueuedSubMesh* qsm = *qi; Region* region = getRegion(qsm->worldBounds, true); region->assign(qsm); } bool stencilShadows = false; if (mCastShadows && mOwner->isShadowTechniqueStencilBased()) { stencilShadows = true; } // Now tell each region to build itself for (RegionMap::iterator ri = mRegionMap.begin(); ri != mRegionMap.end(); ++ri) { ri->second->build(stencilShadows); } } //-------------------------------------------------------------------------- void StaticGeometry::destroy(void) { // delete the regions for (RegionMap::iterator i = mRegionMap.begin(); i != mRegionMap.end(); ++i) { mOwner->extractMovableObject(i->second); delete i->second; } mRegionMap.clear(); } //-------------------------------------------------------------------------- void StaticGeometry::reset(void) { destroy(); for (QueuedSubMeshList::iterator i = mQueuedSubMeshes.begin(); i != mQueuedSubMeshes.end(); ++i) { delete *i; } mQueuedSubMeshes.clear(); // Delete precached geoemtry lists for (SubMeshGeometryLookup::iterator l = mSubMeshGeometryLookup.begin(); l != mSubMeshGeometryLookup.end(); ++l) { delete l->second; } mSubMeshGeometryLookup.clear(); // Delete optimised geometry for (OptimisedSubMeshGeometryList::iterator o = mOptimisedSubMeshGeometryList.begin(); o != mOptimisedSubMeshGeometryList.end(); ++o) { delete *o; } mOptimisedSubMeshGeometryList.clear(); } //-------------------------------------------------------------------------- void StaticGeometry::setVisible(bool visible) { mVisible = visible; // tell any existing regions for (RegionMap::iterator ri = mRegionMap.begin(); ri != mRegionMap.end(); ++ri) { ri->second->setVisible(visible); } } //-------------------------------------------------------------------------- void StaticGeometry::setCastShadows(bool castShadows) { mCastShadows = castShadows; // tell any existing regions for (RegionMap::iterator ri = mRegionMap.begin(); ri != mRegionMap.end(); ++ri) { ri->second->setCastShadows(castShadows); } } //-------------------------------------------------------------------------- void StaticGeometry::setRenderQueueGroup(uint8 queueID) { mRenderQueueIDSet = true; mRenderQueueID = queueID; // tell any existing regions for (RegionMap::iterator ri = mRegionMap.begin(); ri != mRegionMap.end(); ++ri) { ri->second->setRenderQueueGroup(queueID); } } //-------------------------------------------------------------------------- uint8 StaticGeometry::getRenderQueueGroup(void) const { return mRenderQueueID; } //-------------------------------------------------------------------------- void StaticGeometry::dump(const String& filename) const { std::ofstream of(filename.c_str()); of << "Static Geometry Report for " << mName << std::endl; of << "-------------------------------------------------" << std::endl; of << "Number of queued submeshes: " << mQueuedSubMeshes.size() << std::endl; of << "Number of regions: " << mRegionMap.size() << std::endl; of << "Region dimensions: " << mRegionDimensions << std::endl; of << "Origin: " << mOrigin << std::endl; of << "Max distance: " << mUpperDistance << std::endl; of << "Casts shadows?: " << mCastShadows << std::endl; of << std::endl; for (RegionMap::const_iterator ri = mRegionMap.begin(); ri != mRegionMap.end(); ++ri) { ri->second->dump(of); } of << "-------------------------------------------------" << std::endl; } //-------------------------------------------------------------------------- StaticGeometry::RegionIterator StaticGeometry::getRegionIterator(void) { return RegionIterator(mRegionMap.begin(), mRegionMap.end()); } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- StaticGeometry::Region::Region(StaticGeometry* parent, const String& name, SceneManager* mgr, uint32 regionID, const Vector3& centre) : MovableObject(name), mParent(parent), mSceneMgr(mgr), mNode(0), mRegionID(regionID), mCentre(centre), mBoundingRadius(0.0f), mCurrentLod(0), mLightListUpdated(0), mEdgeList(0), mVertexProgramInUse(false) { // First LOD mandatory, and always from 0 mLodSquaredDistances.push_back(0.0f); } //-------------------------------------------------------------------------- StaticGeometry::Region::~Region() { if (mNode) { mNode->getParentSceneNode()->removeChild(mNode); mSceneMgr->destroySceneNode(mNode->getName()); mNode = 0; } // delete for (LODBucketList::iterator i = mLodBucketList.begin(); i != mLodBucketList.end(); ++i) { delete *i; } mLodBucketList.clear(); for (ShadowRenderableList::iterator s = mShadowRenderables.begin(); s != mShadowRenderables.end(); ++s) { delete *s; } mShadowRenderables.clear(); delete mEdgeList; // no need to delete queued meshes, these are managed in StaticGeometry } //-------------------------------------------------------------------------- uint32 StaticGeometry::Region::getTypeFlags(void) const { return SceneManager::STATICGEOMETRY_TYPE_MASK; } //-------------------------------------------------------------------------- void StaticGeometry::Region::assign(QueuedSubMesh* qmesh) { mQueuedSubMeshes.push_back(qmesh); // update lod distances ushort lodLevels = qmesh->submesh->parent->getNumLodLevels(); assert(qmesh->geometryLodList->size() == lodLevels); while(mLodSquaredDistances.size() < lodLevels) { mLodSquaredDistances.push_back(0.0f); } // Make sure LOD levels are max of all at the requested level for (ushort lod = 1; lod < lodLevels; ++lod) { const MeshLodUsage& meshLod = qmesh->submesh->parent->getLodLevel(lod); mLodSquaredDistances[lod] = std::max(mLodSquaredDistances[lod], meshLod.fromDepthSquared); } // update bounds // Transform world bounds relative to our centre AxisAlignedBox localBounds( qmesh->worldBounds.getMinimum() - mCentre, qmesh->worldBounds.getMaximum() - mCentre); mAABB.merge(localBounds); mBoundingRadius = std::max(mBoundingRadius, localBounds.getMinimum().length()); mBoundingRadius = std::max(mBoundingRadius, localBounds.getMaximum().length()); } //-------------------------------------------------------------------------- void StaticGeometry::Region::build(bool stencilShadows) { // Create a node mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(mName, mCentre); mNode->attachObject(this); // We need to create enough LOD buckets to deal with the highest LOD // we encountered in all the meshes queued for (ushort lod = 0; lod < mLodSquaredDistances.size(); ++lod) { LODBucket* lodBucket = new LODBucket(this, lod, mLodSquaredDistances[lod]); mLodBucketList.push_back(lodBucket); // Now iterate over the meshes and assign to LODs // LOD bucket will pick the right LOD to use QueuedSubMeshList::iterator qi, qiend; qiend = mQueuedSubMeshes.end(); for (qi = mQueuedSubMeshes.begin(); qi != qiend; ++qi) { lodBucket->assign(*qi, lod); } // now build lodBucket->build(stencilShadows); } // Do we need to build an edge list? if (stencilShadows) { EdgeListBuilder eb; size_t vertexSet = 0; LODIterator lodIterator = getLODIterator(); while (lodIterator.hasMoreElements()) { LODBucket* lod = lodIterator.getNext(); LODBucket::MaterialIterator matIt = lod->getMaterialIterator(); while (matIt.hasMoreElements()) { MaterialBucket* mat = matIt.getNext(); MaterialBucket::GeometryIterator geomIt = mat->getGeometryIterator(); // Check if we have vertex programs here Technique* t = mat->getMaterial()->getBestTechnique(); if (t) { Pass* p = t->getPass(0); if (p) { if (p->hasVertexProgram()) { mVertexProgramInUse = true; } } } while (geomIt.hasMoreElements()) { GeometryBucket* geom = geomIt.getNext(); // Check we're dealing with 16-bit indexes here // Since stencil shadows can only deal with 16-bit // More than that and stencil is probably too CPU-heavy // in any case assert(geom->getIndexData()->indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT && "Only 16-bit indexes allowed when using stencil shadows"); eb.addVertexData(geom->getVertexData()); eb.addIndexData(geom->getIndexData(), vertexSet++); } } } mEdgeList = eb.build(); } } //-------------------------------------------------------------------------- const String& StaticGeometry::Region::getMovableType(void) const { static String sType = "StaticGeometry"; return sType; } //-------------------------------------------------------------------------- void StaticGeometry::Region::_notifyCurrentCamera(Camera* cam) { // Calculate squared view depth Vector3 diff = cam->getDerivedPosition() - mCentre; Real squaredDepth = diff.squaredLength(); // Determine whether to still render Real renderingDist = mParent->getRenderingDistance(); if (renderingDist > 0) { // Max distance to still render Real maxDist = renderingDist + mBoundingRadius; if (squaredDepth > Math::Sqr(maxDist)) { mBeyondFarDistance = true; return; } } mBeyondFarDistance = false; // Distance from the edge of the bounding sphere mCamDistanceSquared = squaredDepth - mBoundingRadius * mBoundingRadius; // Clamp to 0 mCamDistanceSquared = std::max(static_cast(0.0), mCamDistanceSquared); // Determine active lod mCurrentLod = mLodSquaredDistances.size() - 1; for (ushort i = 0; i < mLodSquaredDistances.size(); ++i) { if (mLodSquaredDistances[i] > mCamDistanceSquared) { mCurrentLod = i - 1; break; } } } //-------------------------------------------------------------------------- const AxisAlignedBox& StaticGeometry::Region::getBoundingBox(void) const { return mAABB; } //-------------------------------------------------------------------------- Real StaticGeometry::Region::getBoundingRadius(void) const { return mBoundingRadius; } //-------------------------------------------------------------------------- void StaticGeometry::Region::_updateRenderQueue(RenderQueue* queue) { mLodBucketList[mCurrentLod]->addRenderables(queue, mRenderQueueID, mCamDistanceSquared); } //-------------------------------------------------------------------------- bool StaticGeometry::Region::isVisible(void) const { return mVisible && !mBeyondFarDistance; } //-------------------------------------------------------------------------- StaticGeometry::Region::LODIterator StaticGeometry::Region::getLODIterator(void) { return LODIterator(mLodBucketList.begin(), mLodBucketList.end()); } //-------------------------------------------------------------------------- const LightList& StaticGeometry::Region::getLights(void) const { // Make sure we only update this once per frame no matter how many // times we're asked ulong frame = Root::getSingleton().getCurrentFrameNumber(); if (frame > mLightListUpdated) { mLightList = mNode->findLights(mBoundingRadius); mLightListUpdated = frame; } return mLightList; } //-------------------------------------------------------------------------- ShadowCaster::ShadowRenderableListIterator StaticGeometry::Region::getShadowVolumeRenderableIterator( ShadowTechnique shadowTechnique, const Light* light, HardwareIndexBufferSharedPtr* indexBuffer, bool extrude, Real extrusionDistance, unsigned long flags) { assert(indexBuffer && "Only external index buffers are supported right now"); assert((*indexBuffer)->getType() == HardwareIndexBuffer::IT_16BIT && "Only 16-bit indexes supported for now"); // Calculate the object space light details Vector4 lightPos = light->getAs4DVector(); Matrix4 world2Obj = mParentNode->_getFullTransform().inverse(); lightPos = world2Obj * lightPos; // We need to search the edge list for silhouette edges if (!mEdgeList) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "You enabled stencil shadows after the buid process!", "StaticGeometry::Region::getShadowVolumeRenderableIterator"); } // Init shadow renderable list if required bool init = mShadowRenderables.empty(); EdgeData::EdgeGroupList::iterator egi; ShadowRenderableList::iterator si, siend; RegionShadowRenderable* esr = 0; if (init) mShadowRenderables.resize(mEdgeList->edgeGroups.size()); //bool updatedSharedGeomNormals = false; siend = mShadowRenderables.end(); egi = mEdgeList->edgeGroups.begin(); for (si = mShadowRenderables.begin(); si != siend; ++si, ++egi) { if (init) { // Create a new renderable, create a separate light cap if // we're using a vertex program (either for this model, or // for extruding the shadow volume) since otherwise we can // get depth-fighting on the light cap *si = new RegionShadowRenderable(this, indexBuffer, egi->vertexData, mVertexProgramInUse || !extrude); } // Get shadow renderable esr = static_cast(*si); HardwareVertexBufferSharedPtr esrPositionBuffer = esr->getPositionBuffer(); // Extrude vertices in software if required if (extrude) { extrudeVertices(esrPositionBuffer, egi->vertexData->vertexCount, lightPos, extrusionDistance); } } // Calc triangle light facing updateEdgeListLightFacing(mEdgeList, lightPos); // Generate indexes and update renderables generateShadowVolume(mEdgeList, *indexBuffer, light, mShadowRenderables, flags); return ShadowRenderableListIterator(mShadowRenderables.begin(), mShadowRenderables.end()); } //-------------------------------------------------------------------------- EdgeData* StaticGeometry::Region::getEdgeList(void) { return mEdgeList; } //-------------------------------------------------------------------------- void StaticGeometry::Region::dump(std::ofstream& of) const { of << "Region " << mRegionID << std::endl; of << "--------------------------" << std::endl; of << "Centre: " << mCentre << std::endl; of << "Local AABB: " << mAABB << std::endl; of << "Bounding radius: " << mBoundingRadius << std::endl; of << "Number of LODs: " << mLodBucketList.size() << std::endl; for (LODBucketList::const_iterator i = mLodBucketList.begin(); i != mLodBucketList.end(); ++i) { (*i)->dump(of); } of << "--------------------------" << std::endl; } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- StaticGeometry::Region::RegionShadowRenderable::RegionShadowRenderable( Region* parent, HardwareIndexBufferSharedPtr* indexBuffer, const VertexData* vertexData, bool createSeparateLightCap, bool isLightCap) : mParent(parent) { // Initialise render op mRenderOp.indexData = new IndexData(); mRenderOp.indexData->indexBuffer = *indexBuffer; mRenderOp.indexData->indexStart = 0; // index start and count are sorted out later // Create vertex data which just references position component (and 2 component) mRenderOp.vertexData = new VertexData(); mRenderOp.vertexData->vertexDeclaration = HardwareBufferManager::getSingleton().createVertexDeclaration(); mRenderOp.vertexData->vertexBufferBinding = HardwareBufferManager::getSingleton().createVertexBufferBinding(); // Map in position data mRenderOp.vertexData->vertexDeclaration->addElement(0,0,VET_FLOAT3, VES_POSITION); ushort origPosBind = vertexData->vertexDeclaration->findElementBySemantic(VES_POSITION)->getSource(); mPositionBuffer = vertexData->vertexBufferBinding->getBuffer(origPosBind); mRenderOp.vertexData->vertexBufferBinding->setBinding(0, mPositionBuffer); // Map in w-coord buffer (if present) if(!vertexData->hardwareShadowVolWBuffer.isNull()) { mRenderOp.vertexData->vertexDeclaration->addElement(1,0,VET_FLOAT1, VES_TEXTURE_COORDINATES, 0); mWBuffer = vertexData->hardwareShadowVolWBuffer; mRenderOp.vertexData->vertexBufferBinding->setBinding(1, mWBuffer); } // Use same vertex start as input mRenderOp.vertexData->vertexStart = vertexData->vertexStart; if (isLightCap) { // Use original vertex count, no extrusion mRenderOp.vertexData->vertexCount = vertexData->vertexCount; } else { // Vertex count must take into account the doubling of the buffer, // because second half of the buffer is the extruded copy mRenderOp.vertexData->vertexCount = vertexData->vertexCount * 2; if (createSeparateLightCap) { // Create child light cap mLightCap = new RegionShadowRenderable(parent, indexBuffer, vertexData, false, true); } } } //-------------------------------------------------------------------------- StaticGeometry::Region::RegionShadowRenderable::~RegionShadowRenderable() { delete mRenderOp.indexData; delete mRenderOp.vertexData; } //-------------------------------------------------------------------------- void StaticGeometry::Region::RegionShadowRenderable::getWorldTransforms( Matrix4* xform) const { // pretransformed *xform = mParent->_getParentNodeFullTransform(); } //-------------------------------------------------------------------------- const Quaternion& StaticGeometry::Region::RegionShadowRenderable::getWorldOrientation(void) const { return mParent->getParentNode()->_getDerivedOrientation(); } //-------------------------------------------------------------------------- const Vector3& StaticGeometry::Region::RegionShadowRenderable::getWorldPosition(void) const { return mParent->getCentre(); } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- StaticGeometry::LODBucket::LODBucket(Region* parent, unsigned short lod, Real lodDist) : mParent(parent), mLod(lod), mSquaredDistance(lodDist) { } //-------------------------------------------------------------------------- StaticGeometry::LODBucket::~LODBucket() { // delete for (MaterialBucketMap::iterator i = mMaterialBucketMap.begin(); i != mMaterialBucketMap.end(); ++i) { delete i->second; } mMaterialBucketMap.clear(); for(QueuedGeometryList::iterator qi = mQueuedGeometryList.begin(); qi != mQueuedGeometryList.end(); ++qi) { delete *qi; } mQueuedGeometryList.clear(); // no need to delete queued meshes, these are managed in StaticGeometry } //-------------------------------------------------------------------------- void StaticGeometry::LODBucket::assign(QueuedSubMesh* qmesh, ushort atLod) { QueuedGeometry* q = new QueuedGeometry(); mQueuedGeometryList.push_back(q); q->position = qmesh->position; q->orientation = qmesh->orientation; q->scale = qmesh->scale; if (qmesh->geometryLodList->size() > atLod) { // This submesh has enough lods, use the right one q->geometry = &(*qmesh->geometryLodList)[atLod]; } else { // Not enough lods, use the lowest one we have q->geometry = &(*qmesh->geometryLodList)[qmesh->geometryLodList->size() - 1]; } // Locate a material bucket MaterialBucket* mbucket = 0; MaterialBucketMap::iterator m = mMaterialBucketMap.find(qmesh->materialName); if (m != mMaterialBucketMap.end()) { mbucket = m->second; } else { mbucket = new MaterialBucket(this, qmesh->materialName); mMaterialBucketMap[qmesh->materialName] = mbucket; } mbucket->assign(q); } //-------------------------------------------------------------------------- void StaticGeometry::LODBucket::build(bool stencilShadows) { // Just pass this on to child buckets for (MaterialBucketMap::iterator i = mMaterialBucketMap.begin(); i != mMaterialBucketMap.end(); ++i) { i->second->build(stencilShadows); } } //-------------------------------------------------------------------------- void StaticGeometry::LODBucket::addRenderables(RenderQueue* queue, uint8 group, Real camDistanceSquared) { // Just pass this on to child buckets MaterialBucketMap::iterator i, iend; iend = mMaterialBucketMap.end(); for (i = mMaterialBucketMap.begin(); i != iend; ++i) { i->second->addRenderables(queue, group, camDistanceSquared); } } //-------------------------------------------------------------------------- StaticGeometry::LODBucket::MaterialIterator StaticGeometry::LODBucket::getMaterialIterator(void) { return MaterialIterator( mMaterialBucketMap.begin(), mMaterialBucketMap.end()); } //-------------------------------------------------------------------------- void StaticGeometry::LODBucket::dump(std::ofstream& of) const { of << "LOD Bucket " << mLod << std::endl; of << "------------------" << std::endl; of << "Distance: " << Math::Sqrt(mSquaredDistance) << std::endl; of << "Number of Materials: " << mMaterialBucketMap.size() << std::endl; for (MaterialBucketMap::const_iterator i = mMaterialBucketMap.begin(); i != mMaterialBucketMap.end(); ++i) { i->second->dump(of); } of << "------------------" << std::endl; } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- StaticGeometry::MaterialBucket::MaterialBucket(LODBucket* parent, const String& materialName) : mParent(parent), mMaterialName(materialName) { } //-------------------------------------------------------------------------- StaticGeometry::MaterialBucket::~MaterialBucket() { // delete for (GeometryBucketList::iterator i = mGeometryBucketList.begin(); i != mGeometryBucketList.end(); ++i) { delete *i; } mGeometryBucketList.clear(); // no need to delete queued meshes, these are managed in StaticGeometry } //-------------------------------------------------------------------------- void StaticGeometry::MaterialBucket::assign(QueuedGeometry* qgeom) { // Look up any current geometry String formatString = getGeometryFormatString(qgeom->geometry); CurrentGeometryMap::iterator gi = mCurrentGeometryMap.find(formatString); bool newBucket = true; if (gi != mCurrentGeometryMap.end()) { // Found existing geometry, try to assign newBucket = !gi->second->assign(qgeom); // Note that this bucket will be replaced as the 'current' // for this format string below since it's out of space } // Do we need to create a new one? if (newBucket) { GeometryBucket* gbucket = new GeometryBucket(this, formatString, qgeom->geometry->vertexData, qgeom->geometry->indexData); // Add to main list mGeometryBucketList.push_back(gbucket); // Also index in 'current' list mCurrentGeometryMap[formatString] = gbucket; if (!gbucket->assign(qgeom)) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Somehow we couldn't fit the requested geometry even in a " "brand new GeometryBucket!! Must be a bug, please report.", "StaticGeometry::MaterialBucket::assign"); } } } //-------------------------------------------------------------------------- void StaticGeometry::MaterialBucket::build(bool stencilShadows) { mMaterial = MaterialManager::getSingleton().getByName(mMaterialName); if (mMaterial.isNull()) { OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Material '" + mMaterialName + "' not found.", "StaticGeometry::MaterialBucket::build"); } mMaterial->load(); // tell the geometry buckets to build for (GeometryBucketList::iterator i = mGeometryBucketList.begin(); i != mGeometryBucketList.end(); ++i) { (*i)->build(stencilShadows); } } //-------------------------------------------------------------------------- void StaticGeometry::MaterialBucket::addRenderables(RenderQueue* queue, uint8 group, Real camDistanceSquared) { // Determine the current material technique mTechnique = mMaterial->getTechnique( mMaterial->getLodIndexSquaredDepth(camDistanceSquared)); GeometryBucketList::iterator i, iend; iend = mGeometryBucketList.end(); for (i = mGeometryBucketList.begin(); i != iend; ++i) { queue->addRenderable(*i, group); } } //-------------------------------------------------------------------------- String StaticGeometry::MaterialBucket::getGeometryFormatString( SubMeshLodGeometryLink* geom) { // Formulate an identifying string for the geometry format // Must take into account the vertex declaration and the index type // Format is (all lines separated by '|'): // Index type // Vertex element (repeating) // source // semantic // type StringUtil::StrStreamType str; str << geom->indexData->indexBuffer->getType() << "|"; const VertexDeclaration::VertexElementList& elemList = geom->vertexData->vertexDeclaration->getElements(); VertexDeclaration::VertexElementList::const_iterator ei, eiend; eiend = elemList.end(); for (ei = elemList.begin(); ei != eiend; ++ei) { const VertexElement& elem = *ei; str << elem.getSource() << "|"; str << elem.getSource() << "|"; str << elem.getSemantic() << "|"; str << elem.getType() << "|"; } return str.str(); } //-------------------------------------------------------------------------- StaticGeometry::MaterialBucket::GeometryIterator StaticGeometry::MaterialBucket::getGeometryIterator(void) { return GeometryIterator( mGeometryBucketList.begin(), mGeometryBucketList.end()); } //-------------------------------------------------------------------------- void StaticGeometry::MaterialBucket::dump(std::ofstream& of) const { of << "Material Bucket " << mMaterialName << std::endl; of << "--------------------------------------------------" << std::endl; of << "Geometry buckets: " << mGeometryBucketList.size() << std::endl; for (GeometryBucketList::const_iterator i = mGeometryBucketList.begin(); i != mGeometryBucketList.end(); ++i) { (*i)->dump(of); } of << "--------------------------------------------------" << std::endl; } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- StaticGeometry::GeometryBucket::GeometryBucket(MaterialBucket* parent, const String& formatString, const VertexData* vData, const IndexData* iData) : Renderable(), mParent(parent), mFormatString(formatString) { // Clone the structure from the example mVertexData = vData->clone(false); mIndexData = iData->clone(false); mVertexData->vertexCount = 0; mVertexData->vertexStart = 0; mIndexData->indexCount = 0; mIndexData->indexStart = 0; mIndexType = iData->indexBuffer->getType(); // Derive the max vertices if (mIndexType == HardwareIndexBuffer::IT_32BIT) { mMaxVertexIndex = 0xFFFFFFFF; } else { mMaxVertexIndex = 0xFFFF; } // Check to see if we have blend indices / blend weights // remove them if so, they can try to blend non-existent bones! const VertexElement* blendIndices = mVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_INDICES); const VertexElement* blendWeights = mVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_WEIGHTS); if (blendIndices && blendWeights) { assert(blendIndices->getSource() == blendWeights->getSource() && "Blend indices and weights should be in the same buffer"); // Get the source ushort source = blendIndices->getSource(); assert(blendIndices->getSize() + blendWeights->getSize() == mVertexData->vertexBufferBinding->getBuffer(source)->getVertexSize() && "Blend indices and blend buffers should have buffer to themselves!"); // Unset the buffer mVertexData->vertexBufferBinding->unsetBinding(source); // Remove the elements mVertexData->vertexDeclaration->removeElement(VES_BLEND_INDICES); mVertexData->vertexDeclaration->removeElement(VES_BLEND_WEIGHTS); } } //-------------------------------------------------------------------------- StaticGeometry::GeometryBucket::~GeometryBucket() { delete mVertexData; delete mIndexData; } //-------------------------------------------------------------------------- const MaterialPtr& StaticGeometry::GeometryBucket::getMaterial(void) const { return mParent->getMaterial(); } //-------------------------------------------------------------------------- Technique* StaticGeometry::GeometryBucket::getTechnique(void) const { return mParent->getCurrentTechnique(); } //-------------------------------------------------------------------------- void StaticGeometry::GeometryBucket::getRenderOperation(RenderOperation& op) { op.indexData = mIndexData; op.operationType = RenderOperation::OT_TRIANGLE_LIST; op.srcRenderable = this; op.useIndexes = true; op.vertexData = mVertexData; } //-------------------------------------------------------------------------- void StaticGeometry::GeometryBucket::getWorldTransforms(Matrix4* xform) const { // Should be the identity transform, but lets allow transformation of the // nodes the regions are attached to for kicks *xform = mParent->getParent()->getParent()->_getParentNodeFullTransform(); } //-------------------------------------------------------------------------- const Quaternion& StaticGeometry::GeometryBucket::getWorldOrientation(void) const { return Quaternion::IDENTITY; } //-------------------------------------------------------------------------- const Vector3& StaticGeometry::GeometryBucket::getWorldPosition(void) const { return mParent->getParent()->getParent()->getCentre(); } //-------------------------------------------------------------------------- Real StaticGeometry::GeometryBucket::getSquaredViewDepth(const Camera* cam) const { return mParent->getParent()->getSquaredDistance(); } //-------------------------------------------------------------------------- const LightList& StaticGeometry::GeometryBucket::getLights(void) const { return mParent->getParent()->getParent()->getLights(); } //-------------------------------------------------------------------------- bool StaticGeometry::GeometryBucket::getCastsShadows(void) const { return mParent->getParent()->getParent()->getCastShadows(); } //-------------------------------------------------------------------------- bool StaticGeometry::GeometryBucket::assign(QueuedGeometry* qgeom) { // Do we have enough space? if (mVertexData->vertexCount + qgeom->geometry->vertexData->vertexCount > mMaxVertexIndex) { return false; } mQueuedGeometry.push_back(qgeom); mVertexData->vertexCount += qgeom->geometry->vertexData->vertexCount; mIndexData->indexCount += qgeom->geometry->indexData->indexCount; return true; } //-------------------------------------------------------------------------- void StaticGeometry::GeometryBucket::build(bool stencilShadows) { // Ok, here's where we transfer the vertices and indexes to the shared // buffers // Shortcuts VertexDeclaration* dcl = mVertexData->vertexDeclaration; VertexBufferBinding* binds = mVertexData->vertexBufferBinding; // create index buffer, and lock mIndexData->indexBuffer = HardwareBufferManager::getSingleton() .createIndexBuffer(mIndexType, mIndexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY); uint32* p32Dest = 0; uint16* p16Dest = 0; if (mIndexType == HardwareIndexBuffer::IT_32BIT) { p32Dest = static_cast( mIndexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD)); } else { p16Dest = static_cast( mIndexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD)); } // create all vertex buffers, and lock ushort b; ushort posBufferIdx = dcl->findElementBySemantic(VES_POSITION)->getSource(); std::vector destBufferLocks; std::vector bufferElements; for (b = 0; b < binds->getBufferCount(); ++b) { size_t vertexCount = mVertexData->vertexCount; // Need to double the vertex count for the position buffer // if we're doing stencil shadows if (stencilShadows && b == posBufferIdx) { vertexCount = vertexCount * 2; assert(vertexCount <= mMaxVertexIndex && "Index range exceeded when using stencil shadows, consider " "reducing your region size or reducing poly count"); } HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( dcl->getVertexSize(b), vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY); binds->setBinding(b, vbuf); uchar* pLock = static_cast( vbuf->lock(HardwareBuffer::HBL_DISCARD)); destBufferLocks.push_back(pLock); // Pre-cache vertex elements per buffer bufferElements.push_back(dcl->findElementsBySource(b)); } // Iterate over the geometry items size_t indexOffset = 0; QueuedGeometryList::iterator gi, giend; giend = mQueuedGeometry.end(); Vector3 regionCentre = mParent->getParent()->getParent()->getCentre(); for (gi = mQueuedGeometry.begin(); gi != giend; ++gi) { QueuedGeometry* geom = *gi; // Copy indexes across with offset IndexData* srcIdxData = geom->geometry->indexData; if (mIndexType == HardwareIndexBuffer::IT_32BIT) { // Lock source indexes uint32* pSrc = static_cast( srcIdxData->indexBuffer->lock( srcIdxData->indexStart, srcIdxData->indexCount, HardwareBuffer::HBL_READ_ONLY)); copyIndexes(pSrc, p32Dest, srcIdxData->indexCount, indexOffset); p32Dest += srcIdxData->indexCount; srcIdxData->indexBuffer->unlock(); } else { // Lock source indexes uint16* pSrc = static_cast( srcIdxData->indexBuffer->lock( srcIdxData->indexStart, srcIdxData->indexCount, HardwareBuffer::HBL_READ_ONLY)); copyIndexes(pSrc, p16Dest, srcIdxData->indexCount, indexOffset); p16Dest += srcIdxData->indexCount; srcIdxData->indexBuffer->unlock(); } // Now deal with vertex buffers // we can rely on buffer counts / formats being the same VertexData* srcVData = geom->geometry->vertexData; VertexBufferBinding* srcBinds = srcVData->vertexBufferBinding; for (b = 0; b < binds->getBufferCount(); ++b) { // lock source HardwareVertexBufferSharedPtr srcBuf = srcBinds->getBuffer(b); uchar* pSrcBase = static_cast( srcBuf->lock(HardwareBuffer::HBL_READ_ONLY)); // Get buffer lock pointer, we'll update this later uchar* pDstBase = destBufferLocks[b]; size_t bufInc = srcBuf->getVertexSize(); // Iterate over vertices float *pSrcReal, *pDstReal; Vector3 tmp; for (size_t v = 0; v < srcVData->vertexCount; ++v) { // Iterate over vertex elements VertexDeclaration::VertexElementList& elems = bufferElements[b]; VertexDeclaration::VertexElementList::iterator ei; for (ei = elems.begin(); ei != elems.end(); ++ei) { VertexElement& elem = *ei; elem.baseVertexPointerToElement(pSrcBase, &pSrcReal); elem.baseVertexPointerToElement(pDstBase, &pDstReal); switch (elem.getSemantic()) { case VES_POSITION: tmp.x = *pSrcReal++; tmp.y = *pSrcReal++; tmp.z = *pSrcReal++; // transform tmp = (geom->orientation * (tmp * geom->scale)) + geom->position; // Adjust for region centre tmp -= regionCentre; *pDstReal++ = tmp.x; *pDstReal++ = tmp.y; *pDstReal++ = tmp.z; break; case VES_NORMAL: tmp.x = *pSrcReal++; tmp.y = *pSrcReal++; tmp.z = *pSrcReal++; // rotation only tmp = geom->orientation * tmp; *pDstReal++ = tmp.x; *pDstReal++ = tmp.y; *pDstReal++ = tmp.z; break; default: // just raw copy memcpy(pDstReal, pSrcReal, VertexElement::getTypeSize(elem.getType())); break; }; } // Increment both pointers pDstBase += bufInc; pSrcBase += bufInc; } // Update pointer destBufferLocks[b] = pDstBase; srcBuf->unlock(); } indexOffset += geom->geometry->vertexData->vertexCount; } // Unlock everything mIndexData->indexBuffer->unlock(); for (b = 0; b < binds->getBufferCount(); ++b) { binds->getBuffer(b)->unlock(); } // If we're dealing with stencil shadows, copy the position data from // the early half of the buffer to the latter part if (stencilShadows) { HardwareVertexBufferSharedPtr buf = binds->getBuffer(posBufferIdx); void* pSrc = buf->lock(HardwareBuffer::HBL_NORMAL); // Point dest at second half (remember vertexcount is original count) void* pDest = static_cast(pSrc) + buf->getVertexSize() * mVertexData->vertexCount; memcpy(pDest, pSrc, buf->getVertexSize() * mVertexData->vertexCount); buf->unlock(); // Also set up hardware W buffer if appropriate RenderSystem* rend = Root::getSingleton().getRenderSystem(); if (rend && rend->getCapabilities()->hasCapability(RSC_VERTEX_PROGRAM)) { buf = HardwareBufferManager::getSingleton().createVertexBuffer( sizeof(float), mVertexData->vertexCount * 2, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); // Fill the first half with 1.0, second half with 0.0 float *pW = static_cast( buf->lock(HardwareBuffer::HBL_DISCARD)); size_t v; for (v = 0; v < mVertexData->vertexCount; ++v) { *pW++ = 1.0f; } for (v = 0; v < mVertexData->vertexCount; ++v) { *pW++ = 0.0f; } buf->unlock(); mVertexData->hardwareShadowVolWBuffer = buf; } } } //-------------------------------------------------------------------------- void StaticGeometry::GeometryBucket::dump(std::ofstream& of) const { of << "Geometry Bucket" << std::endl; of << "---------------" << std::endl; of << "Format string: " << mFormatString << std::endl; of << "Geometry items: " << mQueuedGeometry.size() << std::endl; of << "Vertex count: " << mVertexData->vertexCount << std::endl; of << "Index count: " << mIndexData->indexCount << std::endl; of << "---------------" << std::endl; } //-------------------------------------------------------------------------- }