#include "MeshTree.h"

//---------------------------------------------------------------------------

/*
   Based "The Creation and Rendering of Realistic Trees" article by Jason Weber and Joseph Penn
   Based on a port of Delphi code from TReal project by Ton van den Heuvel
   For further information go see:
   http://members.chello.nl/~l.vandenheuvel2/TReal/
   Copyright (c) 2002-2003, Ton van den Heuvel
   Copyright (c) 2004, Nicolas Chauvin

   ==================================
   Tree generation classes for Ogre3D
   ==================================

*/

namespace Ogre {

//===========================================================================
// Class Tree
//===========================================================================

Tree::Tree(const String& name, TParameters *pParameters, uchar u8Season,  int iSeed)
{
  if (iSeed == -1)
  {
    srand(time(0));  
  }
  else
  {
    srand(iSeed);
  }
    
  mpParameters = pParameters->Clone();
  mpTrunk = new TStem(this);
  mfMaxX = 0.0;
  mfMaxY = 0.0;
  mfMaxZ = 0.0;
  miTotalStems = 0;
  miTotalVertices = 0;
  miTotalFaces = 0;
  miTotalLeaves = 0;
  miTotalLeavesFaces = 0;
  miTotalCoordFrames = 0;
  mu8Season = u8Season;
  mfScale = mpParameters->mfScale + GetRandomValue(mpParameters->mfScaleV);
  mfTrunkLength = (mpParameters->mafNLength[0] + GetRandomValue(mpParameters->mafNLengthV[0])) * mfScale;
  mfTrunkRadius = mfTrunkLength * mpParameters->mfRatio * mpParameters->mfScale0;
  
  mpTrunk->CreateStructure(NULL, mfTrunkLength, 0, 0);

}

//---------------------------------------------------------------------------

Tree::~Tree()
{ 
  delete mpParameters; 
  delete mpTrunk;
}

//---------------------------------------------------------------------------

void Tree::Grow(void)
{
  // Create trunk
  TSectionFrame treeFrame(Quaternion::IDENTITY, Vector3(0,0,0), Vector3(0,0,0));
  mpTrunk->Grow(treeFrame, mfTrunkRadius, 0); 
}

//---------------------------------------------------------------------------

Real Tree::GetRandomValue(const Real fUpperBound)
{
  int iSign = 1;
  int iPrecision = 1000;

  if (fUpperBound == 0)
  {
    return 0.0;
  }
  else
  {
    if (rand()%2 == 1)
      iSign = -1;
    
    return  iSign*(rand()%iPrecision) * fUpperBound / iPrecision;
  }
}

//---------------------------------------------------------------------------

Ogre::MeshPtr Tree::CreateMesh(const String &name) 
{ 
	//HACK: implement ManualResourceLoader
   Ogre::MeshPtr pMesh = MeshManager::getSingleton().createManual(name,"trees"); 
   SubMesh *pStemsSub, *pLeavesSub, *pCoordFrameSub;

   // Set up vertex data 
   // Use a single shared buffer 
   pMesh->sharedVertexData = new VertexData(); 
   VertexData* vertexData = pMesh->sharedVertexData; 
   // Set up Vertex Declaration 
   VertexDeclaration* vertexDecl = vertexData->vertexDeclaration; 
   size_t currOffset = 0; 
   // We always need positions 
   vertexDecl->addElement(POSITION_BINDING, currOffset, VET_FLOAT3, VES_POSITION); 
   currOffset += VertexElement::getTypeSize(VET_FLOAT3); 

   // normals 
   vertexDecl->addElement(POSITION_BINDING, currOffset, VET_FLOAT3, VES_NORMAL); 
   currOffset += VertexElement::getTypeSize(VET_FLOAT3); 

   // 2D texture coords 
   vertexDecl->addElement(POSITION_BINDING, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); 
   currOffset += VertexElement::getTypeSize(VET_FLOAT2); 

   // vertex color
   vertexDecl->addElement(COLOUR_BINDING, currOffset, VET_COLOUR, VES_DIFFUSE);
   currOffset += VertexElement::getTypeSize(VET_COLOUR); 

   vertexData->vertexCount = miTotalVertices; 

   // Allocate vertex buffer 
   HardwareVertexBufferSharedPtr vbuf = 
      HardwareBufferManager::getSingleton(). 
      createVertexBuffer(vertexDecl->getVertexSize(POSITION_BINDING), vertexData->vertexCount, 
      HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); 

   // Set up the binding (one source only) 
   VertexBufferBinding* binding = vertexData->vertexBufferBinding; 
   binding->setBinding(POSITION_BINDING, vbuf); 

   // Generate vertex data recurcively
   // Lock the whole buffer 
   Real* pVertexArray = static_cast<Real*>( vbuf->lock(HardwareBuffer::HBL_DISCARD) ); 

   HardwareVertexBufferSharedPtr vcolbuf =
          HardwareBufferManager::getSingleton().
          createVertexBuffer(vertexDecl->getVertexSize(COLOUR_BINDING), vertexData->vertexCount, 
          HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); 
        // bind position and diffuses
        binding->setBinding(COLOUR_BINDING, vcolbuf);

   RGBA* pVertexColorArray = static_cast<RGBA*>( vcolbuf->lock(HardwareBuffer::HBL_DISCARD) );
   
   mpTrunk->AddMeshVertices(&pVertexArray, &pVertexColorArray);
   if (miTotalLeaves > 0)
     mpTrunk->AddLeavesVertices(&pVertexArray, &pVertexColorArray, 0);
   if (this->mpParameters->mTreeType == TParameters::Simple)
     mpTrunk->AddCoordFrameVertices(&pVertexArray, &pVertexColorArray);

   // Unlock 
   vbuf->unlock(); 
   vcolbuf->unlock(); 
   
   // Generate face list for the trunk and the stems
   pStemsSub = pMesh->createSubMesh(); 
   pStemsSub->useSharedVertices = true; 

   pStemsSub->indexData->indexCount = 3 * miTotalFaces; 
   pStemsSub->indexData->indexBuffer = HardwareBufferManager::getSingleton(). 
         createIndexBuffer( HardwareIndexBuffer::IT_32BIT /*HardwareIndexBuffer::IT_16BIT */ , 
         pStemsSub->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); 

 //  int v1, v2, v3; 
   HardwareIndexBufferSharedPtr ibuf = pStemsSub->indexData->indexBuffer; 
   // Lock the whole buffer 
   // unsigned short* pFaceIndexes = static_cast<unsigned short*> ( ibuf->lock(HardwareBuffer::HBL_DISCARD) );
   unsigned long* pFaceIndexes = static_cast<unsigned long*> ( ibuf->lock(HardwareBuffer::HBL_DISCARD) ); 

   unsigned long u32VertexIndexOffset = 0;
   mpTrunk->AddMeshFaces(&pFaceIndexes, &u32VertexIndexOffset);
   
   // Unlock 
   ibuf->unlock(); 
   
   // Generate face list for the leaves
   if (miTotalLeaves > 0)
   {
		pLeavesSub = pMesh->createSubMesh(); 
		pLeavesSub->useSharedVertices = true; 

		pLeavesSub->indexData->indexCount = 3 * miTotalLeavesFaces; 
		pLeavesSub->indexData->indexBuffer = HardwareBufferManager::getSingleton(). 
				createIndexBuffer(HardwareIndexBuffer::IT_32BIT /*HardwareIndexBuffer::IT_16BIT*/, 
				pLeavesSub->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); 

		//  int v1, v2, v3; 
		ibuf = pLeavesSub->indexData->indexBuffer; 
		// Lock the whole buffer 
		// pFaceIndexes = static_cast<unsigned short*>( ibuf->lock(HardwareBuffer::HBL_DISCARD) ); 
		pFaceIndexes = static_cast<unsigned long*>( ibuf->lock(HardwareBuffer::HBL_DISCARD) ); 

		mpTrunk->AddLeavesMeshFaces(&pFaceIndexes, &u32VertexIndexOffset);

		// Unlock 
		ibuf->unlock(); 
   }


   // Generate face list for the coordinate frame display
   if (this->mpParameters->mTreeType == TParameters::Simple)
   {
     	pCoordFrameSub = pMesh->createSubMesh(); 
		pCoordFrameSub->useSharedVertices = true; 

		pCoordFrameSub->indexData->indexCount = 3 * 9 * miTotalCoordFrames;   // 9 faces per coord frames
		pCoordFrameSub->indexData->indexBuffer = HardwareBufferManager::getSingleton(). 
				createIndexBuffer(HardwareIndexBuffer::IT_32BIT /*HardwareIndexBuffer::IT_16BIT*/, 
				pCoordFrameSub->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); 

		ibuf = pCoordFrameSub->indexData->indexBuffer; 
		// Lock the whole buffer 
		pFaceIndexes = static_cast<unsigned long*>( ibuf->lock(HardwareBuffer::HBL_DISCARD) ); 

		mpTrunk->AddCoordFrameMeshFaces(&pFaceIndexes, &u32VertexIndexOffset);

		// Unlock 
		ibuf->unlock(); 
   }

   // TODO: Improve AAB + SphereRadius !!!!!!!!!!!
   Vector3 vb1, vb2;
   vb1 = Vector3(-mfMaxX, 0, -mfMaxZ) ;
   vb2 = Vector3(mfMaxX, mfMaxY, mfMaxZ) ;
   pMesh->_setBounds(AxisAlignedBox(vb1, vb2)); 
   pMesh->_setBoundingSphereRadius(Math::Sqrt((vb2-vb1).Vector3::dotProduct(vb2-vb1))/2.0); 

   return pMesh;
}
//---------------------------------------------------------------------------

} // namespace
