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

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

		Author:	Arnaud
		Date:	7/29/2003

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

#ifndef __SkiningTypes__
#define __SkiningTypes__

#if CP_PRAGMA_ONCE
#pragma once
#endif


#include "MCBasicTypes.h"
#include "MCClassArray.h"


#define	kUnusedUint16	0xFFFF

struct IShTokenStream;


/// Simple index class to enforce type checking.
/// Represents the index of a joint in the list of joints affecting a skinable primitive (I3dExSkinable)
///
class TJointIndex : public TMCObject
{
public:
	inline TJointIndex()						: fIndex(kUnusedUint16) {}
	inline explicit TJointIndex(uint16 index)	: fIndex(index)			{}

	inline uint16	GetIndex	() const							{ return fIndex; }
	inline void		SetIndex	(uint16 index)						{ fIndex= index; }
	boolean			IsValid		() const							{ return (fIndex != kUnusedUint16); }
	boolean			IsInvalid	() const							{ return (fIndex == kUnusedUint16); }
	inline boolean  operator ==	(const TJointIndex& other) const	{ return (fIndex == other.fIndex);  }
	inline boolean  operator !=	(const TJointIndex& other) const	{ return (fIndex != other.fIndex);  }
protected:
	uint16 fIndex;
};


/// Describe the skin info for a vertex.
/// It can store multiple weights per joint.
class TVertexSkinInfo : public TMCObject
{
public:
	TVertexSkinInfo(int32 weigthChannelCount=0)
	: fWeigthChannelCount(weigthChannelCount) {}

	TVertexSkinInfo(const TVertexSkinInfo& inVertexSkinInfo) 
		: fPrimaryJointIndex(inVertexSkinInfo.fPrimaryJointIndex.GetIndex()),
		  fWeigthChannelCount(inVertexSkinInfo.fWeigthChannelCount),
		  fJointIndices(inVertexSkinInfo.fJointIndices),
		  fWeights(inVertexSkinInfo.fWeights)
	{}

	boolean					IsValid() const;

	inline TJointIndex		GetPrimaryJointIndex() const						{ return fPrimaryJointIndex; }
	inline void				SetPrimaryJointIndex(TJointIndex primaryJointIndex)	{ fPrimaryJointIndex= primaryJointIndex; }

	void					PreallocateJointCount(int32 count);
	inline int32			GetJointsCount() const								{ return fJointIndices.GetElemCount(); }
	inline TJointIndex		GetJointIndex(int32 index) const					{ return fJointIndices[index]; }

	inline int32			GetChannelCount() const								{ return fWeigthChannelCount; };
	inline void				SetChannelCount(int32 count)
	{
		fWeigthChannelCount = count;
		RemoveAllWeights();
	}

	void					RemoveAllWeights();
	void					RemoveWeight(int32 channelIndex);
	void					RemoveJoint(TJointIndex index);
	void					RemoveJoint(int32 index);

	void					Pack();		// removes zeros

	inline real32			GetWeight(int32 jointIndex, int32 channelIndex) const
	{
		MCAssert(channelIndex >= 0 && channelIndex < fWeigthChannelCount);
		MCAssert(jointIndex >= 0 && jointIndex < (int32)fJointIndices.GetElemCount());
		return fWeights[jointIndex*fWeigthChannelCount + channelIndex];
	}

	inline real32&			GetWeight(int32 jointIndex, int32 channelIndex)
	{
		MCAssert(channelIndex >= 0 && channelIndex < fWeigthChannelCount);
		MCAssert(jointIndex >= 0 && jointIndex < (int32)fJointIndices.GetElemCount());
		return fWeights[jointIndex*fWeigthChannelCount + channelIndex];
	}

	inline void				SetWeight(int32 jointIndex, int32 channelIndex, real32 weigth)
	{
		MCAssert(channelIndex >= 0 && channelIndex < fWeigthChannelCount);
		MCAssert(jointIndex >= 0 && jointIndex < (int32)fJointIndices.GetElemCount());
		fWeights[jointIndex*fWeigthChannelCount + channelIndex] = weigth;
	}

	void					SetWeightNormalized(int32 jointIndex, int32 channelIndex, real32 weigth, const TJointIndex& rootJointIndex);
	int32					FindJointIndex(const TJointIndex& jointIndexToFind) const;
	real32					GetJointWeight(const TJointIndex& jointIndex, int32 channelIndex) const;
	void					SetJointWeight(const TJointIndex& jointIndex, int32 channelIndex, real32 weigth);
	void					SetJointWeightNormalized(const TJointIndex& jointIndex, int32 channelIndex, real32 weigth, const TJointIndex& rootJointIndex);
	void					NormalizeWeights(int32 channelIndex, const TJointIndex& rootJointIndex, const TJointIndex& weightToKeepConst);
	void					NormalizeWeights(int32 channelIndex, const TJointIndex& rootJointIndex, int32 constWeighIndex=-1);
	int32					AddJointWeight(const TJointIndex& jointIndex);

	// -Description:
	//		Computes the average weight of each joint on the given vertices
	// -Parameters:
	//		inJointsCount: number of joints in the skining
	//		channelIndex: which channel to use (if -1 all channel will be computed)
	//		inVerticesWeights: the vertices on which to compute the weight's average
	// -Returns:
	//		outAverageWeights: the average weights of the joints (ordered by jointIndex, with inJointsCount elements)
	void					ComputeAverageJointsWeights(int32 inJointsCount, const TMCClassArray<TVertexSkinInfo>& inVerticesWeights, int32 channelIndex=-1);

	void					CopyDataFrom(const TVertexSkinInfo& src, boolean copyPrimaryJointIndex);

	void					Read(IShTokenStream& stream);
	void					Write(IShTokenStream& stream) const;

	int32					SizeOf();

protected:
	TJointIndex					fPrimaryJointIndex;
	uint16						fWeigthChannelCount;
	TMCArray<TJointIndex>		fJointIndices;
	TMCArray<real32>			fWeights;
};


/// Describes the influence of a joint over a vertex
///
struct TWeightedJoint  : public TMCObject
{
	TWeightedJoint()												{fIndex= TJointIndex(0); fWeight= 1;}
	TWeightedJoint(TJointIndex index, real32 weight)				{fIndex= TJointIndex(index); fWeight= weight;}

	TJointIndex	fIndex;		///< Index of the joint (in I3dExSkinable::GetJoints() )
	real32		fWeight;	///< Weight of the Joint (The sum of the weights of the joints influencing a vertex must be 1).
};






#endif
