Logo Search packages:      
Sourcecode: ogre version File versions

OgreStaticGeometry.cpp

/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2006 Torus Knot Software Ltd
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.

You may alternatively use this source under the terms of a specific version of
the OGRE Unrestricted License provided you have obtained such a license from
Torus Knot Software Ltd.
-----------------------------------------------------------------------------
*/
#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

      //--------------------------------------------------------------------------
00053       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)
      {
      }
      //--------------------------------------------------------------------------
00069       StaticGeometry::~StaticGeometry()
      {
            reset();
      }
      //--------------------------------------------------------------------------
00074       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);

      }
      //--------------------------------------------------------------------------
00117       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);

      }
      //--------------------------------------------------------------------------
00135       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);
      }
      //--------------------------------------------------------------------------
00146       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
                  );
      }
      //--------------------------------------------------------------------------
00158       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;
      }
      //--------------------------------------------------------------------------
00183       StaticGeometry::Region* StaticGeometry::getRegion(uint32 index)
      {
            RegionMap::iterator i = mRegionMap.find(index);
            if (i != mRegionMap.end())
            {
                  return i->second;
            }
            else
            {
                  return 0;
            }

      }
      //--------------------------------------------------------------------------
00197       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<ushort>(ix + REGION_HALF_RANGE);
            y = static_cast<ushort>(iy + REGION_HALF_RANGE);
            z = static_cast<ushort>(iz + REGION_HALF_RANGE);


      }
      //--------------------------------------------------------------------------
00226       uint32 StaticGeometry::packIndex(ushort x, ushort y, ushort z)
      {
            return x + (y << 10) + (z << 20);
      }
      //--------------------------------------------------------------------------
00231       StaticGeometry::Region* StaticGeometry::getRegion(const Vector3& point,
            bool autoCreate)
      {
            ushort x, y, z;
            getRegionIndexes(point, x, y, z);
            return getRegion(x, y, z, autoCreate);
      }
      //--------------------------------------------------------------------------
00239       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<unsigned char*>(
                        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);
      }
      //--------------------------------------------------------------------------
00283       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*
00320       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;
      }
      //--------------------------------------------------------------------------
00388       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<uint32*>(id->indexBuffer->lock(
                        id->indexStart, 
                        id->indexCount * id->indexBuffer->getIndexSize(), 
                        HardwareBuffer::HBL_READ_ONLY));
                  buildIndexRemap(p32, id->indexCount, indexRemap);
                  id->indexBuffer->unlock();
            }
            else
            {
                  p16 = static_cast<uint16*>(id->indexBuffer->lock(
                        id->indexStart, 
                        id->indexCount * id->indexBuffer->getIndexSize(), 
                        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<uchar*>(
                        oldBuf->lock(HardwareBuffer::HBL_READ_ONLY));
                  uchar* pDstBase = static_cast<uchar*>(
                        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<uint32*>(id->indexBuffer->lock(
                        id->indexStart, 
                        id->indexCount * id->indexBuffer->getIndexSize(), 
                        HardwareBuffer::HBL_READ_ONLY));
                  pDst32 = static_cast<uint32*>(ibuf->lock(
                        HardwareBuffer::HBL_DISCARD));
                  remapIndexes(pSrc32, pDst32, indexRemap, id->indexCount);
                  id->indexBuffer->unlock();
                  ibuf->unlock();
            }
            else
            {
                  uint16 *pSrc16, *pDst16;
                  pSrc16 = static_cast<uint16*>(id->indexBuffer->lock(
                        id->indexStart, 
                        id->indexCount * id->indexBuffer->getIndexSize(), 
                        HardwareBuffer::HBL_READ_ONLY));
                  pDst16 = static_cast<uint16*>(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);
      }
      //--------------------------------------------------------------------------
00523       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<Entity*>(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<const SceneNode*>(nodei.getNext());
                  // Add this subnode and its children...
                  addSceneNode( node );
            }
      }
      //--------------------------------------------------------------------------
00548       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);
            }

      }
      //--------------------------------------------------------------------------
00576       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();
      }
      //--------------------------------------------------------------------------
00588       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();

      }
      //--------------------------------------------------------------------------
00614       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);
            }
      }
      //--------------------------------------------------------------------------
00625       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);
            }

      }
      //--------------------------------------------------------------------------
00637     void StaticGeometry::setRenderQueueGroup(uint8 queueID)
      {
            assert(queueID <= RENDER_QUEUE_MAX && "Render queue out of range!");
            mRenderQueueIDSet = true;
            mRenderQueueID = queueID;
            // tell any existing regions
            for (RegionMap::iterator ri = mRegionMap.begin();
                  ri != mRegionMap.end(); ++ri)
            {
                  ri->second->setRenderQueueGroup(queueID);
            }
      }
      //--------------------------------------------------------------------------
00650       uint8 StaticGeometry::getRenderQueueGroup(void) const
      {
            return mRenderQueueID;
      }
      //--------------------------------------------------------------------------
00655       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;
      }
      //--------------------------------------------------------------------------
00675       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), 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

      }
      //--------------------------------------------------------------------------
00719       uint32 StaticGeometry::Region::getTypeFlags(void) const
      {
            return SceneManager::STATICGEOMETRY_TYPE_MASK;
      }
      //--------------------------------------------------------------------------
00724       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());

      }
      //--------------------------------------------------------------------------
00755       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();

            }


      }
      //--------------------------------------------------------------------------
00832       const String& StaticGeometry::Region::getMovableType(void) const
      {
            static String sType = "StaticGeometry";
            return sType;
      }
      //--------------------------------------------------------------------------
00838       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<Real>(0.0), mCamDistanceSquared);

            // Determine active lod
            mCurrentLod = static_cast<ushort>(mLodSquaredDistances.size() - 1);
            for (ushort i = 0; i < mLodSquaredDistances.size(); ++i)
            {
                  if (mLodSquaredDistances[i] > mCamDistanceSquared)
                  {
                        mCurrentLod = i - 1;
                        break;
                  }
            }

      }
      //--------------------------------------------------------------------------
00877       const AxisAlignedBox& StaticGeometry::Region::getBoundingBox(void) const
      {
            return mAABB;
      }
      //--------------------------------------------------------------------------
00882       Real StaticGeometry::Region::getBoundingRadius(void) const
      {
            return mBoundingRadius;
      }
      //--------------------------------------------------------------------------
00887       void StaticGeometry::Region::_updateRenderQueue(RenderQueue* queue)
      {
            mLodBucketList[mCurrentLod]->addRenderables(queue, mRenderQueueID,
                  mCamDistanceSquared);
      }
      //--------------------------------------------------------------------------
00893       bool StaticGeometry::Region::isVisible(void) const
      {
            return mVisible && !mBeyondFarDistance;
      }
      //--------------------------------------------------------------------------
      StaticGeometry::Region::LODIterator
00899       StaticGeometry::Region::getLODIterator(void)
      {
            return LODIterator(mLodBucketList.begin(), mLodBucketList.end());
      }
      //--------------------------------------------------------------------------
      ShadowCaster::ShadowRenderableListIterator
00905       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().inverseAffine();
            lightPos = world2Obj.transformAffine(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<RegionShadowRenderable*>(*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());


      }
      //--------------------------------------------------------------------------
00978       EdgeData* StaticGeometry::Region::getEdgeList(void)
      {
            return mEdgeList;
      }
      //--------------------------------------------------------------------------
00983       bool StaticGeometry::Region::hasEdgeList(void)
      {
            return mEdgeList != 0;
      }
      //--------------------------------------------------------------------------
00988       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();
            // 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;
      }
      //--------------------------------------------------------------------------
01062       void StaticGeometry::Region::RegionShadowRenderable::getWorldTransforms(
            Matrix4* xform) const
      {
            // pretransformed
            *xform = mParent->_getParentNodeFullTransform();
      }
      //--------------------------------------------------------------------------
      const Quaternion&
01070       StaticGeometry::Region::RegionShadowRenderable::getWorldOrientation(void) const
      {
            return mParent->getParentNode()->_getDerivedOrientation();
      }
      //--------------------------------------------------------------------------
      const Vector3&
01076       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
      }
      //--------------------------------------------------------------------------
01107       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);
      }
      //--------------------------------------------------------------------------
01141       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);
            }
      }
      //--------------------------------------------------------------------------
01151       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
01164       StaticGeometry::LODBucket::getMaterialIterator(void)
      {
            return MaterialIterator(
                  mMaterialBucketMap.begin(), mMaterialBucketMap.end());
      }
      //--------------------------------------------------------------------------
01170       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
      }
      //--------------------------------------------------------------------------
01205       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");
                  }
            }
      }
      //--------------------------------------------------------------------------
01237       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);
            }
      }
      //--------------------------------------------------------------------------
01255       void StaticGeometry::MaterialBucket::addRenderables(RenderQueue* queue,
            uint8 group, Real camDistanceSquared)
      {
            // Determine the current material technique
            mTechnique = mMaterial->getBestTechnique(
                  mMaterial->getLodIndexSquaredDepth(camDistanceSquared));

            GeometryBucketList::iterator i, iend;
            iend =  mGeometryBucketList.end();
            for (i = mGeometryBucketList.begin(); i != iend; ++i)
            {
                  queue->addRenderable(*i, group);
            }

      }
      //--------------------------------------------------------------------------
01271       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
01303       StaticGeometry::MaterialBucket::getGeometryIterator(void)
      {
            return GeometryIterator(
                  mGeometryBucketList.begin(), mGeometryBucketList.end());
      }
      //--------------------------------------------------------------------------
01309       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);
            // Close gaps in bindings for effective and safely
            mVertexData->closeGapsInBindings();
            }


      }
      //--------------------------------------------------------------------------
      StaticGeometry::GeometryBucket::~GeometryBucket()
      {
            delete mVertexData;
            delete mIndexData;
      }
      //--------------------------------------------------------------------------
01380       const MaterialPtr& StaticGeometry::GeometryBucket::getMaterial(void) const
      {
            return mParent->getMaterial();
      }
      //--------------------------------------------------------------------------
01385       Technique* StaticGeometry::GeometryBucket::getTechnique(void) const
      {
            return mParent->getCurrentTechnique();
      }
      //--------------------------------------------------------------------------
01390       void StaticGeometry::GeometryBucket::getRenderOperation(RenderOperation& op)
      {
            op.indexData = mIndexData;
            op.operationType = RenderOperation::OT_TRIANGLE_LIST;
            op.srcRenderable = this;
            op.useIndexes = true;
            op.vertexData = mVertexData;
      }
      //--------------------------------------------------------------------------
01399       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();
      }
      //--------------------------------------------------------------------------
01406       const Quaternion& StaticGeometry::GeometryBucket::getWorldOrientation(void) const
      {
            return Quaternion::IDENTITY;
      }
      //--------------------------------------------------------------------------
01411       const Vector3& StaticGeometry::GeometryBucket::getWorldPosition(void) const
      {
            return mParent->getParent()->getParent()->getCentre();
      }
      //--------------------------------------------------------------------------
01416       Real StaticGeometry::GeometryBucket::getSquaredViewDepth(const Camera* cam) const
      {
            return mParent->getParent()->getSquaredDistance();
      }
      //--------------------------------------------------------------------------
01421       const LightList& StaticGeometry::GeometryBucket::getLights(void) const
      {
            return mParent->getParent()->getParent()->queryLights();
      }
      //--------------------------------------------------------------------------
01426       bool StaticGeometry::GeometryBucket::getCastsShadows(void) const
      {
            return mParent->getParent()->getParent()->getCastShadows();
      }
      //--------------------------------------------------------------------------
01431       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;
      }
      //--------------------------------------------------------------------------
01447       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<uint32*>(
                        mIndexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
            }
            else
            {
                  p16Dest = static_cast<uint16*>(
                        mIndexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
            }
            // create all vertex buffers, and lock
            ushort b;
            ushort posBufferIdx = dcl->findElementBySemantic(VES_POSITION)->getSource();

            std::vector<uchar*> destBufferLocks;
            std::vector<VertexDeclaration::VertexElementList> 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<uchar*>(
                        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<uint32*>(
                              srcIdxData->indexBuffer->lock(
                                    srcIdxData->indexStart, 
                                    srcIdxData->indexCount * srcIdxData->indexBuffer->getIndexSize(),
                                    HardwareBuffer::HBL_READ_ONLY));

                        copyIndexes(pSrc, p32Dest, srcIdxData->indexCount, indexOffset);
                        p32Dest += srcIdxData->indexCount;
                        srcIdxData->indexBuffer->unlock();
                  }
                  else
                  {
                        // Lock source indexes
                        uint16* pSrc = static_cast<uint16*>(
                              srcIdxData->indexBuffer->lock(
                              srcIdxData->indexStart, 
                              srcIdxData->indexCount * srcIdxData->indexBuffer->getIndexSize(),
                              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<uchar*>(
                              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<uchar*>(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<float*>(
                              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;
                  }
            }

      }
      //--------------------------------------------------------------------------
01662       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;

      }
      //--------------------------------------------------------------------------

}


Generated by  Doxygen 1.6.0   Back to index