/*-------------------------------------------------------------------------------------
Copyright (c) 2006 John Judnich

This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
-------------------------------------------------------------------------------------*/

/// \file BatchedGeometryHw.cpp
/// \brief BatchedGeometryHw is an extension to PagedGeometry which displays entities as hardware instanced geometry.
//-------------------------------------------------------------------------------------

#include <OgreRoot.h>
#include <OgreRenderSystem.h>
#include <OgreCamera.h>
#include <OgreVector3.h>
#include <OgreQuaternion.h>
#include <OgreSceneNode.h>
#include <OgreString.h>
#include <OgreStringConverter.h>
#include <OgreEntity.h>
#include <OgreSubMesh.h>
#include <OgreSubEntity.h>
#include <OgreMesh.h>
#include <OgreMeshManager.h>
#include <OgreHardwareBufferManager.h>
#include <OgreHardwareBuffer.h>
#include <OgreMaterialManager.h>
#include <OgreMaterial.h>
#include <OgreTechnique.h>
#include <OgrePass.h>
#include <string>

#include "BatchedGeometryHw.h"

#pragma warning(push)
#pragma warning(disable:4244)	// for paged geometry
#include "PagedGeometry.h"
#pragma warning(pop)

using namespace Ogre;
using namespace Forests;


/// For Ogre 1.7.2 and 1.7.3 VertexElementType enum writed as
///
/// VET_FLOAT1 = 0
/// VET_FLOAT2 = 1
/// VET_FLOAT3 = 2
/// VET_FLOAT4 = 3
/// VET_COLOUR = 4
/// VET_SHORT1 = 5
/// VET_SHORT2 = 6
/// VET_SHORT3 = 7
/// VET_SHORT4 = 8
/// VET_UBYTE4 = 9
/// VET_COLOUR_ARGB = 10
/// VET_COLOUR_ABGR = 11
const size_t BatchedGeometryHw::s_vertexType2Size[VET_COLOUR_ABGR + 1] = {
   VertexElement::getTypeSize(VET_FLOAT1),
   VertexElement::getTypeSize(VET_FLOAT2),
   VertexElement::getTypeSize(VET_FLOAT3),
   VertexElement::getTypeSize(VET_FLOAT4),
   VertexElement::getTypeSize(VET_COLOUR),
   VertexElement::getTypeSize(VET_SHORT1),
   VertexElement::getTypeSize(VET_SHORT2),
   VertexElement::getTypeSize(VET_SHORT3),
   VertexElement::getTypeSize(VET_SHORT4),
   VertexElement::getTypeSize(VET_UBYTE4),
   VertexElement::getTypeSize(VET_COLOUR_ARGB),
   VertexElement::getTypeSize(VET_COLOUR_ABGR)
};


//-------------------------------------------------------------------------------------
///
BatchedGeometryHw::BatchedGeometryHw(IdType id, ObjectMemoryManager *objectMemoryManager, uint8 renderQueueId,Ogre::SceneManager *mgr, Ogre::SceneNode *rootSceneNode, bool castsShadows) :
MovableObject(id,objectMemoryManager,renderQueueId),
m_fRadius            (0.f),
m_fMinDistanceSquared(0.f),
m_pSceneMgr          (mgr),
m_pSceneNode         (NULL),
m_pParentSceneNode   (rootSceneNode),
m_bWithinFarDistance (false),
m_Built              (false),
m_vecCenter          (Ogre::Vector3::ZERO),
m_BoundsUndefined    (true)
{
   assert(rootSceneNode);

   if (!castsShadows)
	   setCastShadows (false);
}

//-----------------------------------------------------------------------------
///
BatchedGeometryHw::~BatchedGeometryHw()
{
   clear();
}

//-----------------------------------------------------------------------------
///
const Ogre::String& BatchedGeometryHw::getMovableType() const
{
   static const Ogre::String strType = "BatchedGeometryHw";
   return strType;
}


//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::addEntity(Entity *ent, const Vector3 &position,
                                const Quaternion &orientation,
                                const Vector3 &scale,
                                const Ogre::ColourValue &color)
{
   const MeshPtr &mesh = ent->getMesh();

   //If shared vertex data is used, extract into non-shared data
   extractVertexDataFromShared(mesh);	

   //For each subentity
   for (uint32 i = 0; i < ent->getNumSubEntities(); ++i)
   {
      //Get the subentity
      SubEntity *subEntity = ent->getSubEntity(i);
      SubMesh *subMesh = subEntity->getSubMesh();

      //Generate a format string that uniquely identifies this material & vertex/index format
      if (subMesh->vertexData == NULL)
         OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "SubMesh vertex data not found!", "BatchedGeometryHw::addEntity()");
      String formatStr = getFormatString(subEntity);

      //If a batch using an identical format exists...
      SubBatchHw *batch = 0;
      TSubBatchMap::iterator batchIter = m_mapSubBatch.find(formatStr);
      if (batchIter != m_mapSubBatch.end())
         batch = batchIter->second; //Use the batch
      else  // Otherwise create a new batch
      {
         batch = new SubBatchHw(this, subEntity);
         m_mapSubBatch.insert(std::pair<String, SubBatchHw*>(formatStr, batch));
      }

      //Now add the submesh to the compatible batch
      batch->addSubEntity(subEntity, position, orientation, scale, color);
   }

   //Update bounding box
   Matrix4 mat(orientation);
   mat.setScale(scale);
   AxisAlignedBox entBounds = ent->getMesh()->getBounds();
   entBounds.transform(mat);

   if (m_BoundsUndefined)
   {
      m_boundsAAB.setMinimum(entBounds.getMinimum() + position);
      m_boundsAAB.setMaximum(entBounds.getMaximum() + position);
      m_BoundsUndefined = false;
   }
   else
   {
      Vector3 min = m_boundsAAB.getMinimum();
      Vector3 max = m_boundsAAB.getMaximum();
      min.makeFloor(entBounds.getMinimum() + position);
      max.makeCeil(entBounds.getMaximum() + position);
      m_boundsAAB.setMinimum(min);
      m_boundsAAB.setMaximum(max);
   }
}

uint32 CountUsedVertices(IndexData *id, std::map<uint32, uint32> &ibmap);
/*
//-----------------------------------------------------------------------------
///
uint32 CountUsedVertices(IndexData *id, std::map<uint32, uint32> &ibmap)
{
   uint32 i, count;
   switch (id->indexBuffer->getType()) {
      case HardwareIndexBuffer::IT_16BIT:
         {
            uint16 *data = (uint16*)id->indexBuffer->lock(id->indexStart * sizeof(uint16), 
               id->indexCount * sizeof(uint16), HardwareBuffer::HBL_READ_ONLY);

            for (i = 0; i < id->indexCount; i++) {
               uint16 index = data[i];
               if (ibmap.find(index) == ibmap.end()) ibmap[index] = (uint32)(ibmap.size());
            }
            count = (uint32)ibmap.size();
            id->indexBuffer->unlock();
         }
         break;

      case HardwareIndexBuffer::IT_32BIT:
         {
            uint32 *data = (uint32*)id->indexBuffer->lock(id->indexStart * sizeof(uint32), 
               id->indexCount * sizeof(uint32), HardwareBuffer::HBL_READ_ONLY);

            for (i = 0; i < id->indexCount; i++) {
               uint32 index = data[i];
               if (ibmap.find(index) == ibmap.end()) ibmap[index] = (uint32)(ibmap.size());
            }
            count = (uint32)ibmap.size();
            id->indexBuffer->unlock();
         }
         break;

      default:
         throw new Ogre::Exception(0, "Unknown index buffer type", "Converter.cpp::CountVertices");
         break;
   }

   return count;
}
*/

//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::extractVertexDataFromShared(const Ogre::MeshPtr &mesh)
{
   if (mesh.isNull() || !mesh->sharedVertexData)
      return;

   Mesh::SubMeshIterator subMeshIterator = mesh->getSubMeshIterator();

   // Get shared vertex data
   VertexData *oldVertexData = mesh->sharedVertexData;

   while (subMeshIterator.hasMoreElements()) {
      SubMesh *subMesh = subMeshIterator.getNext();

      // Get index data
      IndexData *indexData = subMesh->indexData;
      HardwareIndexBufferSharedPtr ib = indexData->indexBuffer;

      // Create new nonshared vertex data
      std::map<uint32, uint32> indicesMap;
      VertexData *newVertexData = new VertexData();
      newVertexData->vertexCount = CountUsedVertices(indexData, indicesMap);
      //delete newVertexData->vertexDeclaration;
      newVertexData->vertexDeclaration = oldVertexData->vertexDeclaration->clone();

      // Create new vertex buffers
      uint32 buffersCount = (uint32)oldVertexData->vertexBufferBinding->getBufferCount();
      for (uint32 bufferIndex = 0; bufferIndex < buffersCount; bufferIndex++) {

         // Lock shared vertex buffer
         HardwareVertexBufferSharedPtr oldVertexBuffer = oldVertexData->vertexBufferBinding->getBuffer(bufferIndex);
         size_t vertexSize = oldVertexBuffer->getVertexSize();
         uint8 *oldLock = (uint8*)oldVertexBuffer->lock(0, oldVertexData->vertexCount * vertexSize, HardwareBuffer::HBL_READ_ONLY);

         // Create and lock nonshared vertex buffer
         HardwareVertexBufferSharedPtr newVertexBuffer = HardwareBufferManager::getSingleton().createVertexBuffer(
            vertexSize, newVertexData->vertexCount, oldVertexBuffer->getUsage(), oldVertexBuffer->hasShadowBuffer());
         uint8 *newLock = (uint8*)newVertexBuffer->lock(0, newVertexData->vertexCount * vertexSize, HardwareBuffer::HBL_NORMAL);

         // Copy vertices from shared vertex buffer into nonshared vertex buffer
         std::map<uint32, uint32>::iterator i, iend = indicesMap.end();
         for (i = indicesMap.begin(); i != iend; i++) {
            memcpy(newLock + vertexSize * i->second, oldLock + vertexSize * i->first, vertexSize);
         }

         // Unlock vertex buffers
         oldVertexBuffer->unlock();
         newVertexBuffer->unlock();

         // Bind new vertex buffer
         newVertexData->vertexBufferBinding->setBinding(bufferIndex, newVertexBuffer);
      }

      // Re-create index buffer
      switch (indexData->indexBuffer->getType()) {
         case HardwareIndexBuffer::IT_16BIT:
            {
               uint16 *data = (uint16*)indexData->indexBuffer->lock(indexData->indexStart * sizeof(uint16), 
                  indexData->indexCount * sizeof(uint16), HardwareBuffer::HBL_NORMAL);

               for (uint32 i = 0; i < indexData->indexCount; i++) {
                  data[i] = (uint16)indicesMap[data[i]];
               }

               indexData->indexBuffer->unlock();
            }
            break;

         case HardwareIndexBuffer::IT_32BIT:
            {
               uint32 *data = (uint32*)indexData->indexBuffer->lock(indexData->indexStart * sizeof(uint32), 
                  indexData->indexCount * sizeof(uint32), HardwareBuffer::HBL_NORMAL);

               for (uint32 i = 0; i < indexData->indexCount; i++) {
                  data[i] = (uint32)indicesMap[data[i]];
               }

               indexData->indexBuffer->unlock();
            }
            break;

         default:
            throw new Ogre::Exception(0, "Unknown index buffer type", "Converter.cpp::CountVertices");
            break;
      }

      // Store new attributes
      subMesh->useSharedVertices = false;
      subMesh->vertexData = newVertexData;
   }

   // Release shared vertex data
   delete mesh->sharedVertexData;
   mesh->sharedVertexData = NULL;
}


//-----------------------------------------------------------------------------
///
String BatchedGeometryHw::getFormatString(SubEntity *ent)
{
   char buf[2048];

   // add materialname and buffer type
   int countWritten =  sprintf(buf, "%s|%s|%d", ent->getParent()->getName().c_str(), ent->getMaterialName().c_str(), ent->getSubMesh()->indexData->indexBuffer->getType());

   // now add vertex decl
   const VertexDeclaration::VertexElementList &elemList = ent->getSubMesh()->vertexData->vertexDeclaration->getElements();
   for (VertexDeclaration::VertexElementList::const_iterator i = elemList.begin(), iend = elemList.end(); i != iend; ++i)
   {
      const VertexElement &el = *i;
      countWritten += sprintf(buf + countWritten, "|%d|%d|%d", el.getSource(), el.getSemantic(), el.getType());
   }

   return buf;
}


//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::build()
{
   ///Make sure the batch hasn't already been built
   if (m_Built)
      OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Invalid call to build() - geometry is already batched (call clear() first)", "BatchedGeometryHw::GeomBatch::build()");

   if (!m_mapSubBatch.empty())
   {
      // Finish bounds information
      m_vecCenter = m_boundsAAB.getCenter();                            // Calculate bounds center
      m_boundsAAB.setMinimum(m_boundsAAB.getMinimum() - m_vecCenter);   // Center the bounding box
      m_boundsAAB.setMaximum(m_boundsAAB.getMaximum() - m_vecCenter);	// Center the bounding box
      m_fRadius = m_boundsAAB.getMaximum().length();                    // Calculate BB radius

      // Create scene node
      m_pSceneNode = m_pParentSceneNode->createChildSceneNode(SCENE_STATIC,m_vecCenter);

      //Build each batch
      for (TSubBatchMap::iterator i = m_mapSubBatch.begin(), iend = m_mapSubBatch.end(); i != iend; ++i)
         i->second->build();

      m_pSceneNode->attachObject(this);   // Attach the batch to the scene node

      //Debug
      //m_pSceneNode->showBoundingBox(true);

      m_Built = true;
   }

}


//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::clear()
{
   //Remove the batch from the scene
   if (m_pSceneNode)
   {
      m_pSceneNode->removeAllChildren();
      if (m_pSceneNode->getParent())
         m_pSceneNode->getParentSceneNode()->removeAndDestroyChild(m_pSceneNode);
      else
         m_pSceneMgr->destroySceneNode(m_pSceneNode);

      m_pSceneNode = 0;
   }

   //Reset bounds information
   m_BoundsUndefined = true;
   m_vecCenter = Vector3::ZERO;
   m_fRadius = 0.f;

   //Delete each batch
   for (TSubBatchMap::iterator i = m_mapSubBatch.begin(), iend = m_mapSubBatch.end(); i != iend; ++i)
      delete i->second;
   m_mapSubBatch.clear();

   m_Built = false;
}


//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::_updateRenderQueue(Ogre::RenderQueue* queue, Ogre::Camera *camera, const Ogre::Camera *lodCamera)
{
   assert(isVisible() && "Ogre core code must detect that this MovableObject invisible");

   // SVA speed up adding
   Ogre::RenderQueueGroup *rqg = queue->getQueueGroup(getRenderQueueGroup());
   for (TSubBatchMap::const_iterator i = m_mapSubBatch.begin(), iend = m_mapSubBatch.end(); i != iend; ++i)
   {
       if (!(*i).second->m_Built) continue;

       queue->addRenderable((*i).second, mRenderQueueID, OGRE_RENDERABLE_DEFAULT_PRIORITY);
      // i->second->addSelfToRenderQueue(rqg);
   }

   ////If visible...
   //if (isVisible()){
   //   //Ask each batch to add itself to the render queue if appropriate
   //   for (SubBatchMap::iterator i = subBatchMap.begin(); i != subBatchMap.end(); ++i){
   //      i->second->addSelfToRenderQueue(queue, getRenderQueueGroup());
   //   }
   //}
}


//-----------------------------------------------------------------------------
///
bool BatchedGeometryHw::isVisible()
{
    //TODO: PORT TO OGRE 2.x
    return m_bWithinFarDistance;
   //return mVisible && m_bWithinFarDistance;
}


//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::_notifyCurrentCamera(Camera *cam)
{
   if (getRenderingDistance() == Ogre::Real(0.))
      m_bWithinFarDistance = true;
   else
   {
      //Calculate camera distance
      Vector3 camVec = _convertToLocal(cam->getDerivedPosition()) - m_vecCenter;
      Real centerDistanceSquared = camVec.squaredLength();
      m_fMinDistanceSquared = std::max(Ogre::Real(0.), centerDistanceSquared - (m_fRadius * m_fRadius));
      //Note: centerDistanceSquared measures the distance between the camera and the center of the GeomBatch,
      //while minDistanceSquared measures the closest distance between the camera and the closest edge of the
      //geometry's bounding sphere.

      //Determine whether the BatchedGeometryHw is within the far rendering distance
      m_bWithinFarDistance = m_fMinDistanceSquared <= Math::Sqr(getRenderingDistance());
   }
}


//-----------------------------------------------------------------------------
///
Ogre::Vector3 BatchedGeometryHw::_convertToLocal(const Vector3 &globalVec) const
{
   //Convert from the given global position to the local coordinate system of the parent scene node.
   return (m_pParentSceneNode->getOrientation().Inverse() * globalVec);
}



//=============================================================================
// BatchedGeometryHw::SubBatch implementation
//=============================================================================

struct instance
{
	Ogre::Vector4 m14;
	Ogre::Vector4 M24;
	Ogre::Vector4 M34;
	Ogre::ColourValue color;
};

//-----------------------------------------------------------------------------
///
BatchedGeometryHw::SubBatchHw::SubBatchHw(BatchedGeometryHw *parent, SubEntity *ent) :
	m_pBestTechnique        (NULL),
	m_pVertexData           (0),
	m_pIndexData            (0),
	m_Built                 (false),
	m_RequireVertexColors   (false),
	m_pSubMesh              (0),
	m_pParentGeom           (parent),
	m_instanceCount			(0)
{
   assert(ent);
   m_pSubMesh = ent->getSubMesh();

   const Ogre::MaterialPtr &parentMaterial = ent->getMaterial();
   if (parentMaterial.isNull())
      OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "BatchedGeometryHw. Empty parent material", "BatchedGeometryHw::SubBatch::SubBatch");

   // SVA clone material
   // This function is used to make a single clone of materials used, since the materials
   // will be modified by the batch system (and it wouldn't be good to modify the original materials
   // that the user may be using somewhere else).
   {
      Ogre::String newName = parentMaterial->getName() + "_Batched";
      m_ptrMaterial = MaterialManager::getSingleton().getByName(newName, parentMaterial->getGroup());
      if (m_ptrMaterial.isNull())
         m_ptrMaterial = parentMaterial->clone(newName);
   }

   //Setup vertex/index data structure
   m_pVertexData = m_pSubMesh->vertexData->clone(false);
   m_pIndexData = m_pSubMesh->indexData->clone(false);

   //Remove blend weights from vertex format
   const VertexElement* blendIndices = m_pVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_INDICES);
   const VertexElement* blendWeights = m_pVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_WEIGHTS);
   if (blendIndices && blendWeights)
   {
      //Check for format errors
      assert(blendIndices->getSource() == blendWeights->getSource()
         && "Blend indices and weights should be in the same buffer");
      assert(blendIndices->getSize() + blendWeights->getSize() == m_pVertexData->vertexBufferBinding->getBuffer(blendIndices->getSource())->getVertexSize()
         && "Blend indices and blend buffers should have buffer to themselves!");

      //Remove the blend weights
      m_pVertexData->vertexBufferBinding->unsetBinding(blendIndices->getSource());
      m_pVertexData->vertexDeclaration->removeElement(VES_BLEND_INDICES);
      m_pVertexData->vertexDeclaration->removeElement(VES_BLEND_WEIGHTS);
      m_pVertexData->closeGapsInBindings();
   }

   size_t offset = 0;
   Ogre::VertexDeclaration* vertexDeclaration = m_pVertexData->vertexDeclaration;

   m_instanceSource = vertexDeclaration->getMaxSource() + 1;
   unsigned short index = vertexDeclaration->getNextFreeTextureCoordinate();

   // the per-instance data will hold a 3x4 transform and color.
   vertexDeclaration->addElement (m_instanceSource, offset, VET_FLOAT4,  VES_TEXTURE_COORDINATES, index++);
   offset = vertexDeclaration->getVertexSize (m_instanceSource);

   vertexDeclaration->addElement (m_instanceSource, offset, VET_FLOAT4,  VES_TEXTURE_COORDINATES, index++);
   offset = vertexDeclaration->getVertexSize (m_instanceSource);

   vertexDeclaration->addElement (m_instanceSource, offset, VET_FLOAT4,  VES_TEXTURE_COORDINATES, index++);
   offset = vertexDeclaration->getVertexSize (m_instanceSource);

   vertexDeclaration->addElement (m_instanceSource, offset, VET_FLOAT4,  VES_TEXTURE_COORDINATES, index++);
   offset = vertexDeclaration->getVertexSize (m_instanceSource);
   
   assert (offset == sizeof (instance));
   
}


//-----------------------------------------------------------------------------
///
BatchedGeometryHw::SubBatchHw::~SubBatchHw()
{
   clear();

   OGRE_DELETE m_pVertexData;
   OGRE_DELETE m_pIndexData;
}


//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::SubBatchHw::addSubEntity(SubEntity *ent, const Vector3 &position,
                                             const Quaternion &orientation, const Vector3 &scale,
                                             const Ogre::ColourValue &color, void* userData)
{
   assert(!m_Built);

   //Add this submesh to the queue
   QueuedMesh newMesh(ent->getSubMesh(), position, orientation, scale, color, userData);
   if (color != ColourValue::White)
   {
      m_RequireVertexColors = true;
      VertexElementType format = Root::getSingleton().getRenderSystem()->getColourVertexElementType();
      switch (format)
      {
      case VET_COLOUR_ARGB:
         std::swap(newMesh.color.r, newMesh.color.b);
         break;
      case VET_COLOUR_ABGR:
         break;
      default:
         OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Unknown RenderSystem color format", "BatchedGeometryHw::SubBatch::addSubMesh()");
         break;
      }
   }

   m_queueMesh.push_back(newMesh);
}

//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::SubBatchHw::build()
{
	assert(!m_Built);
   
	m_instanceCount = m_queueMesh.size();
	if (!m_instanceCount) return;
		
	// create the per-instance buffer.
	Ogre::VertexDeclaration* vertexDeclaration = m_pVertexData->vertexDeclaration;
	
	HardwareVertexBufferSharedPtr instanceBuffer = HardwareBufferManager::getSingleton().
		createVertexBuffer ( vertexDeclaration->getVertexSize (m_instanceSource), m_instanceCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);

	m_pVertexData->vertexBufferBinding->setBinding (m_instanceSource, instanceBuffer);
	instanceBuffer->setIsInstanceData (true);
	instanceBuffer->setInstanceDataStepRate (1);

	instance* destination = static_cast<instance*> (instanceBuffer->lock (HardwareBuffer::HBL_DISCARD));

	// for each queued mesh...
	for (size_t i = 0; i < m_instanceCount; ++i)
	{
		const QueuedMesh& mesh = m_queueMesh[i];
		
		Ogre::Matrix4 transform;
		transform.makeTransform (mesh.position, mesh.scale, mesh.orientation);		
		
		// we can save space by uploading only the 3x4 portion of the transform.
		// therefore, we'll use the final row for the color.

		// overwrite 4th row with the color info.
		memcpy (transform[3], &mesh.color, sizeof (mesh.color));

		// copy the 3x4 transform plus color into the vertex buffer.
		memcpy (destination, transform[0], sizeof(instance));
		destination++;
	}

	instanceBuffer->unlock();
		
	m_queueMesh.clear();   // Clear mesh queue
	m_Built = true;

      this->setMaterial(m_ptrMaterial);
   m_pBestTechnique = m_ptrMaterial->getBestTechnique();

}

//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::SubBatchHw::clear()
{
	//If built, delete the batch
	if (m_Built)
	{
		m_Built = false;
		
		//Delete buffers
		m_pIndexData->indexBuffer.setNull();
		m_pVertexData->vertexBufferBinding->unsetAllBindings();
	}

	m_instanceCount = 0;
	m_queueMesh.clear(); // Clear mesh queue
}
/*
//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::SubBatchHw::addSelfToRenderQueue(RenderQueueGroup *rqg)
{
   if (!m_Built)
      return;

   //Update material technique based on camera distance
   m_pBestTechnique = m_ptrMaterial->getBestTechnique(m_ptrMaterial->getLodIndex(
      m_pParentGeom->m_fMinDistanceSquared * m_pParentGeom->m_fMinDistanceSquared));
   rqg->addRenderable(this, m_pBestTechnique, OGRE_RENDERABLE_DEFAULT_PRIORITY);
}*/


//-----------------------------------------------------------------------------
///
void BatchedGeometryHw::SubBatchHw::getRenderOperation(RenderOperation& op)
{
   op.operationType = RenderOperation::OT_TRIANGLE_LIST;
#if OGRE_DEBUG_MODE
   op.srcRenderable = this;
#endif
   op.useIndexes = true;
   op.vertexData = m_pVertexData;
   op.indexData = m_pIndexData;
   op.numberOfInstances = m_instanceCount;
}


//-----------------------------------------------------------------------------
///
Real BatchedGeometryHw::SubBatchHw::getSquaredViewDepth(const Camera* cam) const
{
   Vector3 camVec = m_pParentGeom->_convertToLocal(cam->getDerivedPosition()) - m_pParentGeom->m_vecCenter;
   return camVec.squaredLength();
}

//-----------------------------------------------------------------------------
///
const Ogre::LightList& BatchedGeometryHw::SubBatchHw::getLights(void) const
{
   return m_pParentGeom->queryLights();
}
