/****************************************************************************************

		TreePrim.cpp

		Copyright (c)1990 - 1999 MetaCreations. All rights reserved.

		Author:	Jerome Bignon
		Date:	05/17/99

		Revision History

		when		who			what & why
		==========	==========	======================================================
		08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods
		05/17/99	Jerome		Creation

*****************************************************************************************/
// This version of the file is just to build a Tree primitive without modeler

#include "math.h"

#include "I3DShModule.h"
#include "TreePrim.h"
 
const MCGUID CLSID_TreePrim(R_CLSID_TreePrim);


void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialization here
}

void Extension3DCleanup()
{
	// Perform any nec clean-up here
}

TBasicUnknown* MakeCOMObject(const MCCLSID& classId)	// This method instanciate
{														// the object TreePrim
	TBasicUnknown* res = NULL; 

	if (classId == CLSID_TreePrim) res = new TreePrim;

	return res;
}

// Constructor of the C++ Object :

TreePrim::TreePrim()
{
	fData.fNbBranches=3;
	fData.fAngle = kRealFourthPI;
	fData.fRecursiv = 4; 
	fDataChanged = true;

	fTreeBox.fMin[0]= 
	fTreeBox.fMax[0]= 
	fTreeBox.fMin[1]= 
	fTreeBox.fMax[1]= 
	fTreeBox.fMin[2]= 
	fTreeBox.fMax[2]= 0.0;

	// to build the first mesh
	GetFacetMesh(0, nil);
}

void* TreePrim::GetExtensionDataBuffer()
{
	return &fData;
}
 
MCCOMErr TreePrim::ExtensionDataChanged()
{
	return MC_S_OK;
}

int16 TreePrim::GetResID()
{
	return -1; // always return -1, if you do not have a view
}
  

// I3DExGeometricPrimitive methods
// -- Geometry calls 

TVector3 TreePrim::Rotate( TVector3 &Vertex , TVector3 &angle )
{
	TVector3 VertexRot;

	VertexRot[0] = Vertex[0];
	VertexRot[1] = Vertex[2] * sin( angle[0] ) + Vertex[1] * cos( angle[0] );
	VertexRot[2] = Vertex[2] * cos( angle[0] ) - Vertex[1] * sin( angle[0] );
	
	Vertex[2] = VertexRot[0] * sin( angle[1] ) + VertexRot[2] * cos( angle[1] );
	Vertex[0] = VertexRot[0] * cos( angle[1] ) - VertexRot[2] * sin( angle[1] );
	Vertex[1] = VertexRot[1];	

	VertexRot[0] = Vertex[1] * sin( angle[2] ) + Vertex[0] * cos( angle[2] );
	VertexRot[1] = Vertex[1] * cos( angle[2] ) - Vertex[0] * sin( angle[2] );
	VertexRot[2] = Vertex[2];

	return VertexRot;
} 

TVector3 TreePrim::Normal( TVertex3D	*fVertices )
{
	TVector3 v1,v2,normal;
	int j;

	// Normal vector calculation
	for (j=0; j<3; j++)
	{
		v1[j]=fVertices[1].fVertex[j]-fVertices[0].fVertex[j];
		v2[j]=fVertices[2].fVertex[j]-fVertices[0].fVertex[j];
		normal[j]=v2[(j+1)%3]*v1[(j+2)%3] - v2[(j+2)%3]*v1[(j+1)%3];
	}

	real norm=sqrt(normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]);
	normal[0] /= norm;
	normal[1] /= norm;
	normal[2] /= norm; // Normalization
		
	return normal;
}

/////////////////////////////////////////////////////////////////
// Cube :
// Construct TreePrim for a cube
// size     : size of the cube in the z axis.
// angle    : rotation around (0,0,0).
// translate: vector to translate the cube after the rotation.
// return   : the final translation vector.
/////////////////////////////////////////////////////////////////

#define VERTEXNUMBER 14
#define VERTICESNUMBER 9

const int vertex_index[VERTEXNUMBER][3] = {
	{ 0 , 1 , 4 },
	{ 1 , 5 , 4 },
	{ 1 , 2 , 5 },
	{ 2 , 6 , 5 },
	{ 2 , 3 , 6 },
	{ 3 , 7 , 6 },
	{ 3 , 0 , 7 },
	{ 0 , 4 , 7 },
	{ 0 , 2 , 1 },
	{ 0 , 3 , 2 },	
	{ 4 , 7 , 8 },
	{ 8 , 5 , 4 },
	{ 8 , 6 , 5 },
	{ 6 , 7 , 8 }
	};

const real vertex[VERTICESNUMBER][3] = {
	{  -0.0300f  , -0.0300f , 0.00000f },
	{   0.0300f  , -0.0300f , 0.00000f },
	{   0.0300f  ,  0.0300f , 0.00000f },
	{  -0.0300f  ,  0.0300f , 0.00000f },
	{   0.0000f  , -0.0300f , 1.00000f },
	{   0.0300f  , -0.0300f , 1.00000f },
	{   0.0300f  ,  0.0300f , 1.00000f },
	{  -0.0300f  ,  0.0300f , 1.00000f },
	{   0.0000f  ,  0.0000f , 1.10000f }
	};


TVector3 TreePrim::Cube(FacetMeshAccumulator *accum, 
										 real SizeZ, 
										 real SizeXY,
										 TVector3 &angle, 
										 TVector3 &translate)
{
	int i,j;
	TFacet3D TreeCube;	 
	TVector3 VectorTemp;
	TVector3 Vertices[VERTICESNUMBER];

	// 3D coordinates calculation
	for(i=0;i<VERTICESNUMBER;i++)
	{
		VectorTemp[0] = (vertex[i][0]) ;//+ SizeXY ; // *** BUG With the Bounding BOX
		VectorTemp[1] = (vertex[i][1]) ;//+ SizeXY ; 
		VectorTemp[2] = (vertex[i][2]) * SizeZ / 2 ;

		VectorTemp = Rotate( VectorTemp , angle );
		
		Vertices[i][0] = VectorTemp[0] + translate[0] ;
		Vertices[i][1] = VectorTemp[1] + translate[1] ;
		Vertices[i][2] = VectorTemp[2] + translate[2] ;			

		UpdateBoundingBox( &(Vertices[i]) );
	}

	TreeCube.fUVSpace=0;

	// Insert 3D coordinates -> vertex
	for(i=0;i<VERTEXNUMBER;i++)
	{				
		for(j=0;j<3;j++)
		{			
			TreeCube.fVertices[j].fUV[0]  = 0;
			TreeCube.fVertices[j].fUV[1]  = 0;		 
			TreeCube.fVertices[j].fVertex = Vertices[vertex_index[i][j]]; 
		}	
			
		//Same normal for each of 3 Vertices
		TreeCube.fVertices[0].fNormal = 
		TreeCube.fVertices[1].fNormal = 	
		TreeCube.fVertices[2].fNormal = Normal( &(TreeCube.fVertices[0]) );

		if (accum) accum->AccumulateFacet(&TreeCube);
	}

	// return the last vertex calculated
	return TreeCube.fVertices[2].fVertex; 
}

MCCOMErr TreePrim::GetFacetsTree(FacetMeshAccumulator* accum, int level , TVector3 angle , TVector3 translate )
{
	real RadCoef = kRealTwoPI / ((real)fData.fNbBranches);
	real rLevel = (real) level;
	int i;
	TVector3 NewAngle,NewTranslate;		

	level--;

	for(i=0;i<fData.fNbBranches;i++)
	{		
		NewAngle[0] = angle[0] + fData.fAngle * cos( RadCoef * (real) i );
		NewAngle[1] = angle[1] + fData.fAngle * sin( RadCoef * (real) i );
		NewAngle[2] = angle[2];

		NewTranslate = Cube( accum, ( (rLevel*3.5f) + 2.0f ) / fData.fRecursiv , (rLevel/10.0f) , NewAngle , translate );
				
		if ( level>0 ) GetFacetsTree( accum, level , NewAngle , NewTranslate );

	}
	return MC_S_OK;
}

MCCOMErr TreePrim::UpdateBoundingBox( TVector3 *Coordinate )
{
	int i;
		
	for(i=0;i<3;i++)
	{
		fTreeBox.fMin[i] = (fTreeBox.fMin[i] < (*Coordinate)[i] ) ? fTreeBox.fMin[i] : (*Coordinate)[i];
		fTreeBox.fMax[i] = (fTreeBox.fMax[i] > (*Coordinate)[i] ) ? fTreeBox.fMax[i] : (*Coordinate)[i];			
	}

	return MC_S_OK;
}

void TreePrim::GetBoundingBox(TBBox3D& bbox)
{	
	bbox = fTreeBox;
}


MCCOMErr TreePrim::GetNbrLOD(int16& nbrLod)
{
	nbrLod = 1;
	return MC_S_OK;
}

MCCOMErr TreePrim::GetFacetMesh(uint32 index, FacetMesh** outMesh)
{
	TVector3 angle,translate;

	angle[0]=angle[1]=angle[2]=0;
	translate = angle;

	fTreeBox.fMin[0]= 
	fTreeBox.fMax[0]= 
	fTreeBox.fMin[1]= 
	fTreeBox.fMax[1]= 
	fTreeBox.fMin[2]= 
	fTreeBox.fMax[2]= 0.0;

	FacetMeshAccumulator accum;
	translate = Cube( &accum, 6.0 , 2.0 , angle , translate );
	GetFacetsTree( &accum, fData.fRecursiv , angle , translate );
	if (outMesh)
		accum.MakeFacetMesh(outMesh);
	return MC_S_OK;
}

// -- Shading calls
uint32 TreePrim::GetUVSpaceCount()
{
	return 1; // the tree is describe with only 1 UV-Space
}

MCCOMErr TreePrim::GetUVSpace(uint32 uvSpaceID, UVSpaceInfo* uvSpaceInfo)
{
	if (uvSpaceID == 0)
	{ 
		uvSpaceInfo->fWraparound[0] = false;	// No Wrap around
		uvSpaceInfo->fWraparound[1] = false;
	}
	return MC_S_OK;
}
 
/* SetData : 
return :
1 : datas changed
0 : datas are same
*/
boolean TreePrim::SetData(const TreeData& data)
{
	if((data.fNbBranches!=fData.fNbBranches)||(data.fAngle!= fData.fAngle)||(data.fRecursiv!=fData.fRecursiv))
	{
		fData = (TreeData)data; 
		fDataChanged = true;
		return 1; //new datas
	}
	return 0; //same datas
}


boolean TreePrim::GetIfDataChanged()
{
	if (fDataChanged)
	{ 
		fDataChanged= false; 
		return true; 
	}
	return false;		
}
