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

		I3dShFacetMesh.h
		Copyright: (c) 2001 Eovia Corp. All rights reserved.

		Author:	Charles
		Date:	11/2/2001

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

#ifndef __I3dShFacetMesh__
#define __I3dShFacetMesh__

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "APITypes.h"
#include "BitField.h"
#include "MCColorRGBA.h"
#include "Index.h"
#include "MCCompObj.h"
#include "MCArray.h"
#include "MCClassArray.h"
#include "Vector4.h"
#include "TBBox.h"
#include "MCSMPCountedObject.h"
#include "MCCountedPtrHelper.h"
#include "MCCountedPtrArray.h"
#include "SkiningTypes.h"

class TMCiostream;

//****** Globally Unique Ids *************************************************
MCDEFINE_GUID_H(IID_I3DShFacetMeshPrimitive);
MCDEFINE_GUID_H(IID_IShFacetMeshIterator);
MCDEFINE_GUID_H(IID_IShFacetMeshAccumulator);

struct UVSpaceInfo;

// *******************************************************************
//						TPointMesh Definition
// *******************************************************************

/// These are 5x5 marker bitmap defines stored as enums/int32.
/// Specify these as the fMarkerID field in TPointMesh.  
/// The high 7-bits are reserved for future expansion - always zero these out.
///
enum TMarkerPatterns
{	
	kSelectedHandle		= 0x01FFFFFF,	// full 5*5 square
	kNotSelectedHandle	= 0x01F8C63F,	// empty square
	kFullRoundHandle	= 0x00EFFFEE,
	kFramedRoundHandle	= 0x00EDC76E,
	kSmallSquareHandle	= 0x000739C0,	// full 3*3 square
	kPointHandle		= 0x00001000	// a single point ( centered )
};

/// Used to send a set of points to the interactive renderer
///
class TPointMesh : public TMCObject
{
public:
	TMCArray< TVector3 >		fVertex;
	TMCArray< TMCColorRGBA8 >	fVertexColors;
	TMCArray< int32 >			fVertexIndices; 
	
	// marker id per point ?, should also consider widgets (axis, etc) ?
	TMCArray< uint32 >			fMarkerID; ///< uses TMarkerPatterns enum
	
	void CalcBBox(TBBox3D &bBox)
	{
		const uint32 count= fVertex.GetElemCount();
		for(uint32 ii= 0; ii < count; ii++)
			bBox.AddPoint(fVertex[ii]);
	}
};

// *******************************************************************
//						TSegmentMesh Definition
// *******************************************************************

/// Used to send a set of segments to the interactive renderer
///
class TSegmentMesh : public TMCObject
{	
public:
	TSegmentMesh() :	fDrawInFinalRendering(false) {}

	TMCArray< TVector3 >		fVertex;
	TMCArray< TMCColorRGBA8 >	fVertexColors;
	
	TMCArray< TMCColorRGBA8 >	fSegmentColors;
	TMCArray< TIndex2 >			fSegmentIndices;
	
	boolean						fDrawInFinalRendering;	// true if the lines should appear in final rendering (for instance hairs)

	void CalcBBox(TBBox3D &bBox)
	{
		const uint32 count= fVertex.GetElemCount();
		for(uint32 ii= 0; ii < count; ii++)
			bBox.AddPoint(fVertex[ii]);
	}
};

/// \addtogroup Data 
/// \{

/// These are various flags to communicate object state to the renderer.
///
class TFacetMeshRenderFlags : public TBitField32
{
public:
	enum
	{
		kBackfacedMask 			= ( 1 << 0 ), ///< backfaces removed
		kSortedUVSpace			= ( 1 << 1 )  ///< sorted per uv space ID
	};

	inline TFacetMeshRenderFlags()
		: TBitField32( 0 )
	{ }

	inline boolean IsBackfaced() const					{ return TestMasked( kBackfacedMask ); }

	inline boolean HasBeenSortedPerUVSpaceID() const	{ return TestMasked( kSortedUVSpace ); }
};
/// \}

/// \addtogroup Data 
/// \{

/// Structure to represent facets in a FacetMesh
///
struct Triangle : public TMCObject
{
	inline Triangle() {}
	inline Triangle(uint32 p1,uint32 p2,uint32 p3) : pt1(p1),pt2(p2),pt3(p3) {}

	inline const uint32& operator[](int32 i) const
	{
		MCAssert(i>=0 && i<3);

		uint32* const ptr = (uint32*)this;

		return ptr[i];
	}
	inline uint32& operator[](int32 i)
	{
		MCAssert(i>=0 && i<3);

		uint32* const ptr = (uint32*)this;

		return ptr[i];
	}

	inline boolean operator == (const Triangle& tri) const { return pt1 == tri.pt1 && pt2 == tri.pt2 && pt3 == tri.pt3; }
	inline boolean operator != (const Triangle& tri) const { return pt1 != tri.pt1 || pt2 != tri.pt2 || pt3 != tri.pt3; }

	uint32 pt1,pt2,pt3;
};

/// \}

TMCiostream& operator << (TMCiostream& stream, const Triangle& triangle);
TMCiostream& operator >> (TMCiostream& stream, Triangle& triangle);


/// \addtogroup Data 
/// \{

/// Flags for edges of a TEdgeList. Right now there is only a flag for visibility.
///
class TFacetEdgeFlags : public TBitField32
{
public:
	TFacetEdgeFlags() : TBitField32( 0 )			{ }
	
	boolean IsHidden	() const					{ return TestBit( 0 ); } // hardcoded bit for now
	void	SetHidden	(const boolean hidden)		{ SetBitValue( 0, hidden ); }
	
	// add definitions of bit fields here (line styles, etc)
};
/// \}

/// \addtogroup Data 
/// \{

/// An explicit edge list made from the facet mesh
///
struct TEdgeList : public TMCObject
{
	inline uint32 GetElemCount() const;
	inline void SetElemCount(uint32 elemCount);
	inline void SetElemSpace(uint32 elemSpace);
	
	TMCArray<TIndex2>			fFaceIndices;
	TMCArray<TIndex2>			fVertexIndices;
	TMCArray<TFacetEdgeFlags>	fEdgeFlags;
};
/// \}


uint32 TEdgeList::GetElemCount() const
{
	return fVertexIndices.GetElemCount();
}

void TEdgeList::SetElemCount(uint32 elemCount)
{
	fFaceIndices.SetElemCount(elemCount);
	fVertexIndices.SetElemCount(elemCount);
	fEdgeFlags.SetElemCount(elemCount);
}

void TEdgeList::SetElemSpace(uint32 elemSpace)
{
	fFaceIndices.SetElemSpace(elemSpace);
	fVertexIndices.SetElemSpace(elemSpace);
	fEdgeFlags.SetElemSpace(elemSpace);
}



// *********************************************************************
//					class TFacetCache
//	
/// Generic class from which should be derived the caches used by the
/// interactive renderer
///

// Required here to test if a facetCache is for GL rendering or not.
#define FACET_CACHE_DISPLAY_LIST_ID  0x05

class TFacetCache : public TMCSMPCountedObject
{
public:
	TFacetCache(uint32 identifier) : fIdentifier(identifier) {}

	inline uint32		GetIdentifier() { return fIdentifier; }

  /// Invalidate datas (Olivier)
  virtual void Invalidate(void){};
  virtual void InvalidateColors(void) {}

protected:
	const uint32 fIdentifier; ///< identifier of the renderer which setup the cache
};

/// \addtogroup Data 
/// \{

struct TSubMeshInfo
{
	int32	fStartIndexOfVertices;
	int32	fVertexCount;

	int32	fStartIndexOfTriangles;
	int32	fTriangleCount;
};

struct TShadingDomainCache
{
	TMCArray<TSubMeshInfo>	fSubMeshes;
};

/// structure representing a facet mesh, a representation for triangular polygon data using
/// corresponding arrays of data.  Note the several more routines in PublicUtilities.h for 
/// managing facet meshes.  Be careful about maintaining the polygon equation and edge lists.
///
struct FacetMesh : public TMCSMPCountedObject
{

public:
	static	void	Create(FacetMesh** outMesh)
	{ 
		TMCCountedCreateHelper<FacetMesh> result(outMesh);
		result= new FacetMesh;
	}
	
	void	Clone (FacetMesh** clone);
	
protected:
	FacetMesh() : fPrimitiveIDCount(0) {}
	~FacetMesh()
	{
	  
	}
public:	
	void Append(const FacetMesh& mesh);

    // Check if the values currently stored in the FacetMesh make sense.
    boolean IsFacetMeshValid() const;

	/// you must call this routine any time the facets change, the edge generators rely
	/// on this to generate the edge lists
	inline boolean IsEdgeListInvalid() const
		{ return fEdgeList.GetElemCount() == 0; }
	inline void MarkEdgeListInvalid()
		{ fEdgeList.SetElemCount( 0 ); } // doesn't release mem

	/// you must call this routine any time the facets change, the plane generators rely
	/// on this to generate the polygon equations
	inline boolean IsPolygonEqnListInvalid() const
		{ return fPolygonEqns.GetElemCount() == 0; }
	inline void MarkPolygonEqnListInvalid()
	{ fPolygonEqns.SetElemCount( 0 ); } // doesn't release mem
	
	/// calculate the list of plane equations
	inline void CalcPolygonEqnList()
	{
		// should caller check this or us
		if ( ! IsPolygonEqnListInvalid() )
			return;
		
		// build the plane
		const uint32 numFaces = FacetsNbr();
		
		fPolygonEqns.SetElemCount( numFaces );
		
		for ( uint32 i = 0; i < numFaces; ++i )
		{
			TVector4&		planeEqn 	= fPolygonEqns[ i ];
			TVector3&		faceNormal 	= planeEqn.CastToXYZ();
			const TVector3& vtx0 		= fVertices[ fFacets[i].pt1 ]; // [0] ];
			const TVector3& vtx1 		= fVertices[ fFacets[i].pt2 ]; // [1] ];
			const TVector3& vtx2 		= fVertices[ fFacets[i].pt3 ]; // [2] ];
			
			faceNormal = ( vtx1 - vtx0 ) ^ ( vtx2 - vtx0 );
			
			faceNormal.NormalizeOrReplaceWith( TVector3::kUnitZ ); // may want tolerance here
			
			planeEqn.w = -(vtx0 * faceNormal); 
		}
	}
	
	// g/set counts of the faces/vertices
	inline uint32 VerticesNbr() const  
		{ return fVertices.GetElemCount(); }
	inline uint32 FacetsNbr() const	
		{ return fFacets.GetElemCount();   }

	inline void SetVerticesCount( const uint32 numVertices ) 
		{ 
			fVertices.SetElemCount(numVertices); 
			fNormals.SetElemCount(numVertices); 
			fuv.SetElemCount(numVertices); 

			// do this every time or only if count changes ?
			MarkPolygonEqnListInvalid();
			MarkEdgeListInvalid();
		}
	inline void SetFacetsCount( const uint32 numFacets )   
		{ 
			fFacets.SetElemCount(numFacets);   
			fUVSpaceID.SetElemCount(numFacets); 
			
			// do this every time or only if count changes ?
			MarkPolygonEqnListInvalid();
			MarkEdgeListInvalid();
		}
	
	/// computes the bounding box of the mesh
	void CalcBBox(TBBox3D &bBox)
	{
		const uint32 count= fVertices.GetElemCount();

		if (count>0)
		{
			// start with an empty bounding box
			bBox.fMin = TVector3(1e30f,1e30f,1e30f);
			bBox.fMax = TVector3(-1e30f,-1e30f,-1e30f);

			for(uint32 ii= 0; ii < count; ii++)
				bBox.AddPoint(fVertices[ii]);
		}
		else
		{
			bBox.fMin = TVector3::kZero;
			bBox.fMax = TVector3::kZero;
		}
	}

	/// builds the list of edges for the mesh
	/// if the list is already up to date, nothing is done.
	void	BuildEdgeList();

	// newTriangleIndex is an option array to store the new position of the triangles, if it is
	// empty of return it means that no reindexing took place.
	void ReorderFacetsByUVSpaceID(uint32 uvSpaceCount,TMCArray<int32>* newTriangleIndex = NULL);

	// Dump information about the mesh in CSV format. This  function is used for debugging purposes only.
	void DumpDataToFile(char* fileName) const;

	TFacetMeshRenderFlags	GetRenderFlags()	{ return fRenderFlags;	}
	void					SetRenderFlags(TFacetMeshRenderFlags renderFlags)	{ fRenderFlags = renderFlags; }
		
	TMCArray<Triangle>	fFacets;       ///< facets containing 3 indexes to the points in fVertices
	TMCArray<uint32>	fUVSpaceID;    ///< UVspace ID at each facet
	TMCClassArray<TJointIndex>	fFacetPrimaryJointIndex;	///< Primary joint index of skinned primitive for each facet
	TMCArray<uint32>	fFacetPrimitiveID;	///< An index for each facet cooresponding to the index of a sub primitive of the object (ie. polygon of a vertex object)
	uint32				fPrimitiveIDCount;	///< The count of primitive ids (ie. should be the max id that is found in fFacetPrimitiveID)

	TMCArray<TVector3>	fVertices;     ///< positions of points
	TMCArray<TVector3>	fNormals;      ///< normals at the vertices
	TMCArray<TVector2>	fuv;		   ///< uv space coordinates at each vertices
	TMCArray<TVector3>  fTextureUVW;   ///< solid texture coordinates to clamp procedurals/projections (unsupported)
	TMCArray<TMCColorRGBA8> fVertexColors;	///< when you want to explicitly choose the vertex color
	
	// when you want to explicitly choose the polygon diffuse color, set to black no alpha to use material diffuse
	TMCArray<TMCColorRGBA8> fPolygonColors;		
	TMCArray<TMCColorRGBA8> fPolygonBackColors;	//	fill this array only if you want a different color on the back side
	
	/// these are the plane equations for the polygons (a combo of unit normal and plane coeff)
	/// we use the plane eqn of the form for compute the plane coeff     A x + B y + C z + D = 0
	TMCArray<TVector4>	fPolygonEqns;

	TEdgeList							fEdgeList;		// an explicit edge list

	TFacetMeshRenderFlags				fRenderFlags;

	TMCClassArray<TShadingDomainCache>		fShadingDomains;
	TMCCountedPtrArray<TFacetCache>			fRenderCaches;	// caches that represent the mesh in optimal hardware format
};

inline void FacetMesh::Clone (FacetMesh** clone)
{
	TMCCountedCreateHelper<FacetMesh> result(clone);
	FacetMesh* newMesh= new FacetMesh;
	result= newMesh;

	newMesh->fFacets=fFacets;
	newMesh->fUVSpaceID=fUVSpaceID;
	newMesh->fFacetPrimaryJointIndex=fFacetPrimaryJointIndex;
	newMesh->fFacetPrimitiveID=fFacetPrimitiveID;
	newMesh->fPrimitiveIDCount=fPrimitiveIDCount;
	newMesh->fVertices=fVertices;
	newMesh->fNormals=fNormals;     
	newMesh->fuv=fuv;
	newMesh->fVertexColors=fVertexColors;
	newMesh->fTextureUVW=fTextureUVW;
	newMesh->fPolygonColors=fPolygonColors;		
	newMesh->fPolygonBackColors=fPolygonBackColors;
	
	newMesh->fPolygonEqns=fPolygonEqns;

	newMesh->fEdgeList=fEdgeList;

}
/// \}

/// \addtogroup ExternalInterface
/// \{

// ****************************************************************************
//								I3DShFacetMeshPrimitive
// 
/// This interface is supported only by the facet mesh primitive that is bundled
/// with the application. It can be used from an importer to store a mesh into
/// a facet mesh primitive (family ID 'prim', classID 'fmes').
/// 
struct I3DShFacetMeshPrimitive: public IMCUnknown
{
	// ****************************************************************************
	//								SetBoundingBox
	// 
	/// Sets the bounding of the mesh.
	/// 
	/// \param bbox  The bounding box of the mesh.
	/// 
	virtual void	 MCCOMAPI SetBoundingBox(const TBBox3D &bbox) = 0;
	
	// ****************************************************************************
	//								SetNbrLOD
	// 
	/// Sets the number of levels of details that the primitive contains.
	/// 
	/// \param nbrLod  The number of levels of details.
	/// 
	virtual MCCOMErr MCCOMAPI SetNbrLOD(uint32 nbrLod) = 0;
	
	// ****************************************************************************
	//								SetFacetMesh
	// 
	/// Sets the facet mesh for a given level of detail. Note that the primitive
	/// keeps a reference to the facetmesh so you should not modify it once you
	/// have called this function.
	/// 
	/// \param index  The index of the level of detail (0 based).
	/// \param lod    The precision of the level of detail (0 is the maximum precision).
	/// \param mesh   A point to the facet mesh.
	/// 
	virtual MCCOMErr MCCOMAPI SetFacetMesh(uint32 index,real lod,FacetMesh *mesh) = 0;
	
	// ****************************************************************************
	//								SetUVSpaceCount
	// 
	/// Sets the number of shading domaines (UV spaces).
	/// 
	/// \param nbrUV  The number of shading domaines.
	/// 
	virtual MCCOMErr MCCOMAPI SetUVSpaceCount(int32 nbrUV) = 0;
	
	// ****************************************************************************
	//								SetUVSpace
	// 
	/// Sets the definition of a given shading domaine (UV space).
	/// 
	/// \param index        The 0-based index of the shading domaine.
	/// \param uvSpaceInfo  The definition of the shading domaine.
	/// 
	virtual MCCOMErr MCCOMAPI SetUVSpace(int32 index,const UVSpaceInfo &uvSpaceInfo) = 0;
};

/// \}

#endif
