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

		COMTreePrim.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
		06/29/99	Jonathan	CanbeSplit and SplitPrimitive uses TBasic implementation
		05/17/99	Jerome		Creation
		07/22/99	Mael		added GetPolygonInfo

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

#include "math.h"

#include "TreePrim.h"
#include "ITreePrim.h"
#include "Modeler.h"
#include "ModelerView.h"
#include "MCCountedPtrHelper.h"

const MCGUID CLSID_TreePrim(R_CLSID_TreePrim);
const MCGUID IID_ITreePrim(R_IID_ITreePrim);


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 objects for Modeler
	TBasicUnknown* res = NULL;

	if (classId == CLSID_TreePrim) res = new TreePrim;
	if (classId == CLSID_Modeler) res = new Modeler;
	if (classId == CLSID_ModelerView) res = new ModelerView;

	return res;
}

// Constructor of the C++ Object :

boolean	TreePrim::AutoSwitchToModeler() const 
{
	return TBasicPrimitive::AutoSwitchToModeler();
};



TreePrim::TreePrim()
{
	fData.fNbBranches=3;
	fData.fAngle =  kRealFourthPI;
	fData.fRecursiv = 4;  
	fData.fScalingFactor = 1;
	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);
}

MCCOMErr TreePrim::QueryInterface(const MCIID& riid, void** ppvObj)
{
	if (MCIsEqualIID(riid, IID_ITreePrim))
	{
		TMCCountedGetHelper<ITreePrim>	result(ppvObj);
		result = this;
		return MC_S_OK;
	}
	else
		return TBasicPrimitive::QueryInterface(riid, ppvObj);
}

uint32 TreePrim::AddRef()
{
	return TBasicPrimitive::AddRef();
}


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
}

boolean TreePrim::CanBeSplit()
{
	return TBasicPrimitive::CanBeSplit();
}

MCCOMErr TreePrim::SplitPrimitive(TMCCountedPtrArray<I3DExGeometricPrimitive>& subParts, TMCArray<TTransform3D>& subPartPositions)
{
	return TBasicPrimitive::SplitPrimitive(subParts, subPartPositions);
}

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 COMTreePrim 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 (callback) callback(&TreeCube,privData);    
		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::EnumPatches(EnumPatchesCallback callback, void* privData, boolean& closed)
{
	return TBasicPrimitive::EnumPatches(callback, privData, closed);
}

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

MCCOMErr TreePrim::GetUVSpaceRDS5(uint32 uvSpaceID, UVSpaceInfoRDS5* uvSpaceInfoRDS5)
{
	return TBasicPrimitive::GetUVSpaceRDS5(uvSpaceID,uvSpaceInfoRDS5);
}

MCCOMErr TreePrim::UV2XYZ(TVector2* uv, uint32 uvSpaceID, TVector3* resultPosition, boolean* inUVSpace)
{
	return TBasicPrimitive::UV2XYZ(uv,uvSpaceID,resultPosition,inUVSpace);
}

MCCOMErr TreePrim::AppendToRenderables( const TTransform3D& 	worldFromModelTfm,
										   TRenderableAndTfmArray& renderableAndTfm)
{
	return TBasicPrimitive::AppendToRenderables(worldFromModelTfm,renderableAndTfm);
}

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