/*
-----------------------------------------------------------------------------
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 "OgreManualObject.h"
#include "OgreException.h"
#include "OgreMaterialManager.h"
#include "OgreSceneNode.h"
#include "OgreRoot.h"
#include "OgreRenderSystem.h"
#include "OgreHardwareBufferManager.h"
#include "OgreEdgeListBuilder.h"
#include "OgreMeshManager.h"
#include "OgreMesh.h"
#include "OgreSubMesh.h"

namespace Ogre {

#define TEMP_INITIAL_SIZE 50
#define TEMP_VERTEXSIZE_GUESS sizeof(float) * 12
#define TEMP_INITIAL_VERTEX_SIZE TEMP_VERTEXSIZE_GUESS * TEMP_INITIAL_SIZE
#define TEMP_INITIAL_INDEX_SIZE sizeof(uint32) * TEMP_INITIAL_SIZE
	//-----------------------------------------------------------------------------
	ManualObject::ManualObject(const String& name)
		: MovableObject(name),
		  mDynamic(false), mCurrentSection(0), mFirstVertex(true),
		  mTempVertexPending(false),
		  mTempVertexBuffer(0), mTempVertexSize(TEMP_INITIAL_VERTEX_SIZE),
		  mTempIndexBuffer(0), mTempIndexSize(TEMP_INITIAL_INDEX_SIZE),
		  mDeclSize(0), mEstVertexCount(0), mEstIndexCount(0), mTexCoordIndex(0), 
		  mRadius(0), mAnyIndexed(false), mEdgeList(0), 
		  mUseIdentityProjection(false), mUseIdentityView(false), mKeepDeclarationOrder(false)
	{
	}
	//-----------------------------------------------------------------------------
	ManualObject::~ManualObject()
	{
		clear();
	}
	//-----------------------------------------------------------------------------
	void ManualObject::clear(void)
	{
		resetTempAreas();
		for (SectionList::iterator i = mSectionList.begin(); i != mSectionList.end(); ++i)
		{
			OGRE_DELETE *i;
		}
		mSectionList.clear();
		mRadius = 0;
		mAABB.setNull();
		OGRE_DELETE mEdgeList;
		mEdgeList = 0;
		mAnyIndexed = false;
		for (ShadowRenderableList::iterator s = mShadowRenderables.begin();
			s != mShadowRenderables.end(); ++s)
		{
			OGRE_DELETE *s;
		}
		mShadowRenderables.clear();


	}
	//-----------------------------------------------------------------------------
	void ManualObject::resetTempAreas(void)
	{
		OGRE_FREE(mTempVertexBuffer, MEMCATEGORY_GEOMETRY);
		OGRE_FREE(mTempIndexBuffer, MEMCATEGORY_GEOMETRY);
		mTempVertexBuffer = 0;
		mTempIndexBuffer = 0;
		mTempVertexSize = TEMP_INITIAL_VERTEX_SIZE;
		mTempIndexSize = TEMP_INITIAL_INDEX_SIZE;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::resizeTempVertexBufferIfNeeded(size_t numVerts)
	{
		// Calculate byte size
		// Use decl if we know it by now, otherwise default size to pos/norm/texcoord*2
		size_t newSize;
		if (!mFirstVertex)
		{
			newSize = mDeclSize * numVerts;
		}
		else
		{
			// estimate - size checks will deal for subsequent verts
			newSize = TEMP_VERTEXSIZE_GUESS * numVerts;
		}
		if (newSize > mTempVertexSize || !mTempVertexBuffer)
		{
			if (!mTempVertexBuffer)
			{
				// init
				newSize = mTempVertexSize;
			}
			else
			{
				// increase to at least double current
				newSize = std::max(newSize, mTempVertexSize*2);
			}
			// copy old data
			char* tmp = mTempVertexBuffer;
			mTempVertexBuffer = OGRE_ALLOC_T(char, newSize, MEMCATEGORY_GEOMETRY);
			if (tmp)
			{
				memcpy(mTempVertexBuffer, tmp, mTempVertexSize);
				// delete old buffer
				OGRE_FREE(tmp, MEMCATEGORY_GEOMETRY);
			}
			mTempVertexSize = newSize;
		}
	}
	//-----------------------------------------------------------------------------
	void ManualObject::resizeTempIndexBufferIfNeeded(size_t numInds)
	{
		size_t newSize = numInds * sizeof(uint32);
		if (newSize > mTempIndexSize || !mTempIndexBuffer)
		{
			if (!mTempIndexBuffer)
			{
				// init
				newSize = mTempIndexSize;
			}
			else
			{
				// increase to at least double current
				newSize = std::max(newSize, mTempIndexSize*2);
			}
			numInds = newSize / sizeof(uint32);
			uint32* tmp = mTempIndexBuffer;
			mTempIndexBuffer = OGRE_ALLOC_T(uint32, numInds, MEMCATEGORY_GEOMETRY);
			if (tmp)
			{
				memcpy(mTempIndexBuffer, tmp, mTempIndexSize);
				OGRE_FREE(tmp, MEMCATEGORY_GEOMETRY);
			}
			mTempIndexSize = newSize;
		}

	}
	//-----------------------------------------------------------------------------
	void ManualObject::estimateVertexCount(size_t vcount)
	{
		resizeTempVertexBufferIfNeeded(vcount);
		mEstVertexCount = vcount;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::estimateIndexCount(size_t icount)
	{
		resizeTempIndexBufferIfNeeded(icount);
		mEstIndexCount = icount;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::begin(const String& materialName,
		RenderOperation::OperationType opType)
	{
		if (mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You cannot call begin() again until after you call end()",
				"ManualObject::begin");
		}
		mCurrentSection = OGRE_NEW ManualObjectSection(this, materialName, opType);
		mCurrentUpdating = false;
		mCurrentSection->setUseIdentityProjection(mUseIdentityProjection);
		mCurrentSection->setUseIdentityView(mUseIdentityView);
		mSectionList.push_back(mCurrentSection);
		mFirstVertex = true;
		mDeclSize = 0;
		mTexCoordIndex = 0;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::beginUpdate(size_t sectionIndex)
	{
		if (mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You cannot call begin() again until after you call end()",
				"ManualObject::beginUpdate");
		}
		if (sectionIndex >= mSectionList.size())
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"Invalid section index - out of range.",
				"ManualObject::beginUpdate");
		}
		mCurrentSection = mSectionList[sectionIndex];
		mCurrentUpdating = true;
		mFirstVertex = true;
		mTexCoordIndex = 0;
		// reset vertex & index count
		RenderOperation* rop = mCurrentSection->getRenderOperation();
		rop->vertexData->vertexCount = 0;
		if (rop->indexData)
			rop->indexData->indexCount = 0;
		rop->useIndexes = false;
		mDeclSize = rop->vertexData->vertexDeclaration->getVertexSize(0);
	}
	//-----------------------------------------------------------------------------
	void ManualObject::position(const Vector3& pos)
	{
		position(pos.x, pos.y, pos.z);
	}
	//-----------------------------------------------------------------------------
	void ManualObject::position(Real x, Real y, Real z)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You must call begin() before this method",
				"ManualObject::position");
		}
		if (mTempVertexPending)
		{
			// bake current vertex
			copyTempVertexToBuffer();
			mFirstVertex = false;
		}

		if (mFirstVertex && !mCurrentUpdating)
		{
			// defining declaration
			mCurrentSection->getRenderOperation()->vertexData->vertexDeclaration
				->addElement(0, mDeclSize, VET_FLOAT3, VES_POSITION);
			mDeclSize += VertexElement::getTypeSize(VET_FLOAT3);
		}

		mTempVertex.position.x = x;
		mTempVertex.position.y = y;
		mTempVertex.position.z = z;

		// update bounds
		mAABB.merge(mTempVertex.position);
		mRadius = std::max(mRadius, mTempVertex.position.length());

		// reset current texture coord
		mTexCoordIndex = 0;

		mTempVertexPending = true;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::normal(const Vector3& norm)
	{
		normal(norm.x, norm.y, norm.z);
	}
	//-----------------------------------------------------------------------------
	void ManualObject::normal(Real x, Real y, Real z)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You must call begin() before this method",
				"ManualObject::normal");
		}
		if (mFirstVertex && !mCurrentUpdating)
		{
			// defining declaration
			mCurrentSection->getRenderOperation()->vertexData->vertexDeclaration
				->addElement(0, mDeclSize, VET_FLOAT3, VES_NORMAL);
			mDeclSize += VertexElement::getTypeSize(VET_FLOAT3);
		}
		mTempVertex.normal.x = x;
		mTempVertex.normal.y = y;
		mTempVertex.normal.z = z;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::textureCoord(Real u)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You must call begin() before this method",
				"ManualObject::textureCoord");
		}
		if (mFirstVertex && !mCurrentUpdating)
		{
			// defining declaration
			mCurrentSection->getRenderOperation()->vertexData->vertexDeclaration
				->addElement(0, mDeclSize, VET_FLOAT1, VES_TEXTURE_COORDINATES, mTexCoordIndex);
			mDeclSize += VertexElement::getTypeSize(VET_FLOAT1);
		}
		mTempVertex.texCoordDims[mTexCoordIndex] = 1;
		mTempVertex.texCoord[mTexCoordIndex].x = u;

		++mTexCoordIndex;

	}
	//-----------------------------------------------------------------------------
	void ManualObject::textureCoord(Real u, Real v)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You must call begin() before this method",
				"ManualObject::textureCoord");
		}
		if (mFirstVertex && !mCurrentUpdating)
		{
			// defining declaration
			mCurrentSection->getRenderOperation()->vertexData->vertexDeclaration
				->addElement(0, mDeclSize, VET_FLOAT2, VES_TEXTURE_COORDINATES, mTexCoordIndex);
			mDeclSize += VertexElement::getTypeSize(VET_FLOAT2);
		}
		mTempVertex.texCoordDims[mTexCoordIndex] = 2;
		mTempVertex.texCoord[mTexCoordIndex].x = u;
		mTempVertex.texCoord[mTexCoordIndex].y = v;

		++mTexCoordIndex;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::textureCoord(Real u, Real v, Real w)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You must call begin() before this method",
				"ManualObject::textureCoord");
		}
		if (mFirstVertex && !mCurrentUpdating)
		{
			// defining declaration
			mCurrentSection->getRenderOperation()->vertexData->vertexDeclaration
				->addElement(0, mDeclSize, VET_FLOAT3, VES_TEXTURE_COORDINATES, mTexCoordIndex);
			mDeclSize += VertexElement::getTypeSize(VET_FLOAT3);
		}
		mTempVertex.texCoordDims[mTexCoordIndex] = 3;
		mTempVertex.texCoord[mTexCoordIndex].x = u;
		mTempVertex.texCoord[mTexCoordIndex].y = v;
		mTempVertex.texCoord[mTexCoordIndex].z = w;

		++mTexCoordIndex;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::textureCoord(Real x, Real y, Real z, Real w)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You must call begin() before this method",
				"ManualObject::textureCoord");
		}
		if (mFirstVertex && !mCurrentUpdating)
		{
			// defining declaration
			mCurrentSection->getRenderOperation()->vertexData->vertexDeclaration
				->addElement(0, mDeclSize, VET_FLOAT4, VES_TEXTURE_COORDINATES, mTexCoordIndex);
			mDeclSize += VertexElement::getTypeSize(VET_FLOAT4);
		}
		mTempVertex.texCoordDims[mTexCoordIndex] = 4;
		mTempVertex.texCoord[mTexCoordIndex].x = x;
		mTempVertex.texCoord[mTexCoordIndex].y = y;
		mTempVertex.texCoord[mTexCoordIndex].z = z;
		mTempVertex.texCoord[mTexCoordIndex].w = w;

		++mTexCoordIndex;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::textureCoord(const Vector2& uv)
	{
		textureCoord(uv.x, uv.y);
	}
	//-----------------------------------------------------------------------------
	void ManualObject::textureCoord(const Vector3& uvw)
	{
		textureCoord(uvw.x, uvw.y, uvw.z);
	}
	//---------------------------------------------------------------------
	void ManualObject::textureCoord(const Vector4& xyzw)
	{
		textureCoord(xyzw.x, xyzw.y, xyzw.z, xyzw.w);
	}
	//-----------------------------------------------------------------------------
	void ManualObject::colour(const ColourValue& col)
	{
		colour(col.r, col.g, col.b, col.a);
	}
	//-----------------------------------------------------------------------------
	void ManualObject::colour(Real r, Real g, Real b, Real a)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You must call begin() before this method",
				"ManualObject::colour");
		}
		if (mFirstVertex && !mCurrentUpdating)
		{
			// defining declaration
			mCurrentSection->getRenderOperation()->vertexData->vertexDeclaration
				->addElement(0, mDeclSize, VET_COLOUR, VES_DIFFUSE);
			mDeclSize += VertexElement::getTypeSize(VET_COLOUR);
		}
		mTempVertex.colour.r = r;
		mTempVertex.colour.g = g;
		mTempVertex.colour.b = b;
		mTempVertex.colour.a = a;

	}
	//-----------------------------------------------------------------------------
	void ManualObject::index(uint32 idx)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You must call begin() before this method",
				"ManualObject::index");
		}
		mAnyIndexed = true;
		if (idx >= 65536)
			mCurrentSection->set32BitIndices(true);

		// make sure we have index data
		RenderOperation* rop = mCurrentSection->getRenderOperation();
		if (!rop->indexData)
		{
			rop->indexData = OGRE_NEW IndexData();
			rop->indexData->indexCount = 0;
		}
		rop->useIndexes = true;
		resizeTempIndexBufferIfNeeded(++rop->indexData->indexCount);

		mTempIndexBuffer[rop->indexData->indexCount - 1] = idx;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::triangle(uint32 i1, uint32 i2, uint32 i3)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You must call begin() before this method",
				"ManualObject::index");
		}
		if (mCurrentSection->getRenderOperation()->operationType !=
			RenderOperation::OT_TRIANGLE_LIST)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"This method is only valid on triangle lists",
				"ManualObject::index");
		}

		index(i1);
		index(i2);
		index(i3);
	}
	//-----------------------------------------------------------------------------
	void ManualObject::quad(uint32 i1, uint32 i2, uint32 i3, uint32 i4)
	{
		// first tri
		triangle(i1, i2, i3);
		// second tri
		triangle(i3, i4, i1);
	}
	//-----------------------------------------------------------------------------
	void ManualObject::copyTempVertexToBuffer(void)
	{
		mTempVertexPending = false;
		RenderOperation* rop = mCurrentSection->getRenderOperation();
		if (rop->vertexData->vertexCount == 0 && !mCurrentUpdating)
		{
			// first vertex, autoorganise decl
			VertexDeclaration* oldDcl = rop->vertexData->vertexDeclaration;
			rop->vertexData->vertexDeclaration =
				oldDcl->getAutoOrganisedDeclaration(false, false);
			HardwareBufferManager::getSingleton().destroyVertexDeclaration(oldDcl);
		}
		resizeTempVertexBufferIfNeeded(++rop->vertexData->vertexCount);

		// get base pointer
		char* pBase = mTempVertexBuffer + (mDeclSize * (rop->vertexData->vertexCount-1));
		const VertexDeclaration::VertexElementList& elemList =
			rop->vertexData->vertexDeclaration->getElements();
		for (VertexDeclaration::VertexElementList::const_iterator i = elemList.begin();
			i != elemList.end(); ++i)
		{
			float* pFloat = 0;
			RGBA* pRGBA = 0;
			const VertexElement& elem = *i;
			switch(elem.getType())
			{
			case VET_FLOAT1:
			case VET_FLOAT2:
			case VET_FLOAT3:
			case VET_FLOAT4:
				elem.baseVertexPointerToElement(pBase, &pFloat);
				break;
			case VET_COLOUR:
			case VET_COLOUR_ABGR:
			case VET_COLOUR_ARGB:
				elem.baseVertexPointerToElement(pBase, &pRGBA);
				break;
			default:
				// nop ?
				break;
			};


			RenderSystem* rs;
			unsigned short dims;
			switch(elem.getSemantic())
			{
			case VES_POSITION:
				*pFloat++ = mTempVertex.position.x;
				*pFloat++ = mTempVertex.position.y;
				*pFloat++ = mTempVertex.position.z;
				break;
			case VES_NORMAL:
				*pFloat++ = mTempVertex.normal.x;
				*pFloat++ = mTempVertex.normal.y;
				*pFloat++ = mTempVertex.normal.z;
				break;
			case VES_TEXTURE_COORDINATES:
				dims = VertexElement::getTypeCount(elem.getType());
				for (ushort t = 0; t < dims; ++t)
					*pFloat++ = mTempVertex.texCoord[elem.getIndex()][t];
				break;
			case VES_DIFFUSE:
				rs = Root::getSingleton().getRenderSystem();
				if (rs)
					rs->convertColourValue(mTempVertex.colour, pRGBA++);
				else
					*pRGBA++ = mTempVertex.colour.getAsRGBA(); // pick one!
				break;
			default:
				// nop ?
				break;
			};

		}

	}
	//-----------------------------------------------------------------------------
	ManualObject::ManualObjectSection* ManualObject::end(void)
	{
		if (!mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You cannot call end() until after you call begin()",
				"ManualObject::end");
		}
		if (mTempVertexPending)
		{
			// bake current vertex
			copyTempVertexToBuffer();
		}

		// pointer that will be returned
		ManualObjectSection* result = NULL;

		RenderOperation* rop = mCurrentSection->getRenderOperation();
		// Check for empty content
		if (rop->vertexData->vertexCount == 0 ||
			(rop->useIndexes && rop->indexData->indexCount == 0))
		{
			// You're wasting my time sonny
			if (mCurrentUpdating)
			{
				// Can't just undo / remove since may be in the middle
				// Just allow counts to be 0, will not be issued to renderer

				// return the finished section (though it has zero vertices)
				result = mCurrentSection;
			}
			else
			{
				// First creation, can really undo
				// Has already been added to section list end, so remove
				mSectionList.pop_back();
				OGRE_DELETE mCurrentSection;

			}
		}
		else // not an empty section
		{

			// Bake the real buffers
			HardwareVertexBufferSharedPtr vbuf;
			// Check buffer sizes
			bool vbufNeedsCreating = true;
			bool ibufNeedsCreating = rop->useIndexes;
            // Work out if we require 16 or 32-bit index buffers
            HardwareIndexBuffer::IndexType indexType = mCurrentSection->get32BitIndices()?  
				HardwareIndexBuffer::IT_32BIT : HardwareIndexBuffer::IT_16BIT;
			if (mCurrentUpdating)
			{
				// May be able to reuse buffers, check sizes
				vbuf = rop->vertexData->vertexBufferBinding->getBuffer(0);
				if (vbuf->getNumVertices() >= rop->vertexData->vertexCount)
					vbufNeedsCreating = false;

				if (rop->useIndexes)
				{
					if ((rop->indexData->indexBuffer->getNumIndexes() >= rop->indexData->indexCount) &&
                        (indexType == rop->indexData->indexBuffer->getType()))
						ibufNeedsCreating = false;
				}

			}
			if (vbufNeedsCreating)
			{
				// Make the vertex buffer larger if estimated vertex count higher
				// to allow for user-configured growth area
				size_t vertexCount = std::max(rop->vertexData->vertexCount, 
					mEstVertexCount);
				vbuf =
					HardwareBufferManager::getSingleton().createVertexBuffer(
						mDeclSize,
						vertexCount,
						mDynamic? HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY : 
							HardwareBuffer::HBU_STATIC_WRITE_ONLY);
				rop->vertexData->vertexBufferBinding->setBinding(0, vbuf);
			}
			if (ibufNeedsCreating)
			{
				// Make the index buffer larger if estimated index count higher
				// to allow for user-configured growth area
				size_t indexCount = std::max(rop->indexData->indexCount, 
					mEstIndexCount);
				rop->indexData->indexBuffer =
					HardwareBufferManager::getSingleton().createIndexBuffer(
						indexType,
						indexCount,
						mDynamic? HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY : 
							HardwareBuffer::HBU_STATIC_WRITE_ONLY);
			}
			// Write vertex data
			vbuf->writeData(
				0, rop->vertexData->vertexCount * vbuf->getVertexSize(), 
				mTempVertexBuffer, true);
			// Write index data
			if(rop->useIndexes)
			{
                if (HardwareIndexBuffer::IT_32BIT == indexType)
                {
                    // direct copy from the mTempIndexBuffer
				    rop->indexData->indexBuffer->writeData(
					    0, 
					    rop->indexData->indexCount 
						    * rop->indexData->indexBuffer->getIndexSize(),
					    mTempIndexBuffer, true);
                }
                else //(HardwareIndexBuffer::IT_16BIT == indexType)
                {
					uint16* pIdx = static_cast<uint16*>(rop->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
					uint32* pSrc = mTempIndexBuffer;
                    for (size_t i = 0; i < rop->indexData->indexCount; i++)
                    {
                        *pIdx++ = static_cast<uint16>(*pSrc++);
                    }
					rop->indexData->indexBuffer->unlock();

                }
			}

			// return the finished section
			result = mCurrentSection;

		} // empty section check

		mCurrentSection = 0;
		resetTempAreas();

		// Tell parent if present
		if (mParentNode)
		{
			mParentNode->needUpdate();
		}

		// will return the finished section or NULL if
		// the section was empty (i.e. zero vertices/indices)
		return result;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::setMaterialName(size_t idx, const String& name)
	{
		if (idx >= mSectionList.size())
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"Index out of bounds!",
				"ManualObject::setMaterialName");
		}

		mSectionList[idx]->setMaterialName(name);

	}
	//-----------------------------------------------------------------------------
	MeshPtr ManualObject::convertToMesh(const String& meshName, const String& groupName)
	{
		if (mCurrentSection)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"You cannot call convertToMesh() whilst you are in the middle of "
				"defining the object; call end() first.",
				"ManualObject::convertToMesh");
		}
		if (mSectionList.empty())
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"No data defined to convert to a mesh.",
				"ManualObject::convertToMesh");
		}
		MeshPtr m = MeshManager::getSingleton().createManual(meshName, groupName);

		for (SectionList::iterator i = mSectionList.begin(); i != mSectionList.end(); ++i)
		{
			ManualObjectSection* sec = *i;
			RenderOperation* rop = sec->getRenderOperation();
			SubMesh* sm = m->createSubMesh();
			sm->useSharedVertices = false;
			sm->operationType = rop->operationType;
			sm->setMaterialName(sec->getMaterialName());
			// Copy vertex data; replicate buffers too
			sm->vertexData = rop->vertexData->clone(true);
			// Copy index data; replicate buffers too; delete the default, old one to avoid memory leaks

			// check if index data is present
			if (rop->indexData)
			{
				// Copy index data; replicate buffers too; delete the default, old one to avoid memory leaks
				OGRE_DELETE sm->indexData;
				sm->indexData = rop->indexData->clone(true);
			}
		}
        // update bounds
		m->_setBounds(mAABB);
		m->_setBoundingSphereRadius(mRadius);

		m->load();

		return m;


	}
	//-----------------------------------------------------------------------------
	void ManualObject::setUseIdentityProjection(bool useIdentityProjection)
	{
		// Set existing
		for (SectionList::iterator i = mSectionList.begin(); i != mSectionList.end(); ++i)
		{
			(*i)->setUseIdentityProjection(useIdentityProjection);
		}
		
		// Save setting for future sections
		mUseIdentityProjection = useIdentityProjection;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::setUseIdentityView(bool useIdentityView)
	{
		// Set existing
		for (SectionList::iterator i = mSectionList.begin(); i != mSectionList.end(); ++i)
		{
			(*i)->setUseIdentityView(useIdentityView);
		}

		// Save setting for future sections
		mUseIdentityView = useIdentityView;
	}
    //-----------------------------------------------------------------------
	ManualObject::ManualObjectSection* ManualObject::getSection(unsigned int index) const
    {
        if (index >= mSectionList.size())
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
            "Index out of bounds.",
            "ManualObject::getSection");
        return mSectionList[index];
    }
    //-----------------------------------------------------------------------
	unsigned int ManualObject::getNumSections(void) const
    {
        return static_cast< unsigned int >( mSectionList.size() );
    }
	//-----------------------------------------------------------------------------
	const String& ManualObject::getMovableType(void) const
	{
		return ManualObjectFactory::FACTORY_TYPE_NAME;
	}
	//-----------------------------------------------------------------------------
	const AxisAlignedBox& ManualObject::getBoundingBox(void) const
	{
		return mAABB;
	}
	//-----------------------------------------------------------------------------
	Real ManualObject::getBoundingRadius(void) const
	{
		return mRadius;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::_updateRenderQueue(RenderQueue* queue)
	{
		// To be used when order of creation must be kept while rendering
		unsigned short priority = queue->getDefaultRenderablePriority();

		for (SectionList::iterator i = mSectionList.begin(); i != mSectionList.end(); ++i)
		{
			// Skip empty sections (only happens if non-empty first, then updated)
			RenderOperation* rop = (*i)->getRenderOperation();
			if (rop->vertexData->vertexCount == 0 ||
				(rop->useIndexes && rop->indexData->indexCount == 0))
				continue;

			if (mRenderQueueIDSet)
				queue->addRenderable(*i, mRenderQueueID, mKeepDeclarationOrder ? priority++ : queue->getDefaultRenderablePriority());
			else
				queue->addRenderable(*i, queue->getDefaultQueueGroup(), mKeepDeclarationOrder ? priority++ : queue->getDefaultRenderablePriority());
		}
	}
	//-----------------------------------------------------------------------------
	void ManualObject::visitRenderables(Renderable::Visitor* visitor, 
		bool debugRenderables)
	{
		for (SectionList::iterator i = mSectionList.begin(); i != mSectionList.end(); ++i)
		{
			visitor->visit(*i, 0, false);
		}

	}
	//-----------------------------------------------------------------------------
	EdgeData* ManualObject::getEdgeList(void)
	{
		// Build on demand
		if (!mEdgeList && mAnyIndexed)
		{
			EdgeListBuilder eb;
			size_t vertexSet = 0;
			bool anyBuilt = false;
			for (SectionList::iterator i = mSectionList.begin(); i != mSectionList.end(); ++i)
			{
				RenderOperation* rop = (*i)->getRenderOperation();
				// Only indexed triangle geometry supported for stencil shadows
				if (rop->useIndexes && rop->indexData->indexCount != 0 && 
					(rop->operationType == RenderOperation::OT_TRIANGLE_FAN ||
					 rop->operationType == RenderOperation::OT_TRIANGLE_LIST ||
					 rop->operationType == RenderOperation::OT_TRIANGLE_STRIP))
				{
					eb.addVertexData(rop->vertexData);
					eb.addIndexData(rop->indexData, vertexSet++);
					anyBuilt = true;
				}
			}

			if (anyBuilt)
				mEdgeList = eb.build();

		}
		return mEdgeList;
	}
	//---------------------------------------------------------------------
	bool ManualObject::hasEdgeList()
	{
		return getEdgeList() != 0;
	}
	//-----------------------------------------------------------------------------
	ShadowCaster::ShadowRenderableListIterator
	ManualObject::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");		

        EdgeData* edgeList = getEdgeList();
        if (!edgeList)
        {
            return ShadowRenderableListIterator(
                mShadowRenderables.begin(), mShadowRenderables.end());
        }

		// Calculate the object space light details
		Vector4 lightPos = light->getAs4DVector();
		Matrix4 world2Obj = mParentNode->_getFullTransform().inverseAffine();
		lightPos = world2Obj.transformAffine(lightPos);


		// Init shadow renderable list if required (only allow indexed)
		bool init = mShadowRenderables.empty() && mAnyIndexed;

		EdgeData::EdgeGroupList::iterator egi;
		ShadowRenderableList::iterator si, siend;
		ManualObjectSectionShadowRenderable* esr = 0;
		SectionList::iterator seci;
		if (init)
			mShadowRenderables.resize(edgeList->edgeGroups.size());

		siend = mShadowRenderables.end();
		egi = edgeList->edgeGroups.begin();
		seci = mSectionList.begin();
		for (si = mShadowRenderables.begin(); si != siend; ++seci)
		{
            // Skip non-indexed geometry
            if (!(*seci)->getRenderOperation()->useIndexes)
            {
                continue;
            }

			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
				MaterialPtr mat = (*seci)->getMaterial();
				mat->load();
				bool vertexProgram = false;
				Technique* t = mat->getBestTechnique(0, *seci);
				for (int p = 0; p < t->getNumPasses(); ++p)
				{
					Pass* pass = t->getPass(p);
					if (pass->hasVertexProgram())
					{
						vertexProgram = true;
						break;
					}
				}
				*si = OGRE_NEW ManualObjectSectionShadowRenderable(this, indexBuffer,
					egi->vertexData, vertexProgram || !extrude);
			}
			// Get shadow renderable
			esr = static_cast<ManualObjectSectionShadowRenderable*>(*si);
			HardwareVertexBufferSharedPtr esrPositionBuffer = esr->getPositionBuffer();
			// Extrude vertices in software if required
			if (extrude)
			{
				extrudeVertices(esrPositionBuffer,
					egi->vertexData->vertexCount,
					lightPos, extrusionDistance);

			}

            ++si;
            ++egi;
		}
		// Calc triangle light facing
		updateEdgeListLightFacing(edgeList, lightPos);

		// Generate indexes and update renderables
		generateShadowVolume(edgeList, *indexBuffer, light,
			mShadowRenderables, flags);


		return ShadowRenderableListIterator(
			mShadowRenderables.begin(), mShadowRenderables.end());


	}
	//-----------------------------------------------------------------------------
	//-----------------------------------------------------------------------------
	//-----------------------------------------------------------------------------
	ManualObject::ManualObjectSection::ManualObjectSection(ManualObject* parent,
		const String& materialName,	RenderOperation::OperationType opType)
		: mParent(parent), mMaterialName(materialName), m32BitIndices(false)
	{
		mRenderOperation.operationType = opType;
		// default to no indexes unless we're told
		mRenderOperation.useIndexes = false;
		mRenderOperation.vertexData = OGRE_NEW VertexData();
		mRenderOperation.vertexData->vertexCount = 0;

	}
	//-----------------------------------------------------------------------------
	ManualObject::ManualObjectSection::~ManualObjectSection()
	{
		OGRE_DELETE mRenderOperation.vertexData;
		OGRE_DELETE mRenderOperation.indexData; // ok to delete 0
	}
	//-----------------------------------------------------------------------------
	RenderOperation* ManualObject::ManualObjectSection::getRenderOperation(void)
	{
		return &mRenderOperation;
	}
	//-----------------------------------------------------------------------------
	const MaterialPtr& ManualObject::ManualObjectSection::getMaterial(void) const
	{
		if (mMaterial.isNull())
		{
			// Load from default group. If user wants to use alternate groups,
			// they can define it and preload
			mMaterial = MaterialManager::getSingleton().load(mMaterialName,
				ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
		}
		return mMaterial;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::ManualObjectSection::setMaterialName(const String& name)
	{
		if (mMaterialName != name)
		{
			mMaterialName = name;
			mMaterial.setNull();
		}
	}
	//-----------------------------------------------------------------------------
	void ManualObject::ManualObjectSection::getRenderOperation(RenderOperation& op)
	{
		// direct copy
		op = mRenderOperation;
	}
	//-----------------------------------------------------------------------------
	void ManualObject::ManualObjectSection::getWorldTransforms(Matrix4* xform) const
	{
		xform[0] = mParent->_getParentNodeFullTransform();
	}
	//-----------------------------------------------------------------------------
	Real ManualObject::ManualObjectSection::getSquaredViewDepth(const Ogre::Camera *cam) const
	{
		Node* n = mParent->getParentNode();
		assert(n);
		return n->getSquaredViewDepth(cam);
	}
	//-----------------------------------------------------------------------------
	const LightList& ManualObject::ManualObjectSection::getLights(void) const
	{
		return mParent->queryLights();
	}
	//-----------------------------------------------------------------------------
	//--------------------------------------------------------------------------
	ManualObject::ManualObjectSectionShadowRenderable::ManualObjectSectionShadowRenderable(
		ManualObject* parent, HardwareIndexBufferSharedPtr* indexBuffer,
		const VertexData* vertexData, bool createSeparateLightCap,
		bool isLightCap)
		: mParent(parent)
	{
		// Initialise render op
		mRenderOp.indexData = OGRE_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 = OGRE_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 = OGRE_NEW ManualObjectSectionShadowRenderable(parent,
					indexBuffer, vertexData, false, true);
			}
		}
	}
	//--------------------------------------------------------------------------
	ManualObject::ManualObjectSectionShadowRenderable::~ManualObjectSectionShadowRenderable()
	{
		OGRE_DELETE mRenderOp.indexData;
		OGRE_DELETE mRenderOp.vertexData;
	}
	//--------------------------------------------------------------------------
	void ManualObject::ManualObjectSectionShadowRenderable::getWorldTransforms(
		Matrix4* xform) const
	{
		// pretransformed
		*xform = mParent->_getParentNodeFullTransform();
	}
	//-----------------------------------------------------------------------------
	//-----------------------------------------------------------------------------
	String ManualObjectFactory::FACTORY_TYPE_NAME = "ManualObject";
	//-----------------------------------------------------------------------------
	const String& ManualObjectFactory::getType(void) const
	{
		return FACTORY_TYPE_NAME;
	}
	//-----------------------------------------------------------------------------
	MovableObject* ManualObjectFactory::createInstanceImpl(
		const String& name, const NameValuePairList* params)
	{
		return OGRE_NEW ManualObject(name);
	}
	//-----------------------------------------------------------------------------
	void ManualObjectFactory::destroyInstance( MovableObject* obj)
	{
		OGRE_DELETE obj;
	}



}
