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

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

		Author:	Arnaud
		Date:	11/10/2001

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

#ifndef __I3dExSkinable__
#define __I3dExSkinable__

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCCompObj.h"
#include "APITypes.h"
#include "SkiningTypes.h"
#include "TransformsFwd.h"


MCDEFINE_GUID_SAFELIB(IID_I3dExSkinable, 0xd15f4820, 0xf276, 0x4a76, 0x9a, 0x45, 0x2e, 0x8d, 0x49, 0xea, 0x9f, 0xc3);


struct I3DShJoint;
struct I3DShGlobalDeformerHelper;

template <class T> class TMCPtrArray;



/// \addtogroup ExternalInterface
///
/// \{


enum ESkinableDisplayMode
{
	kNormalMode,
	kBoundingBoxesOnlyMode,
};

enum ESkinnableSelectionMode
{
	kSelectPrimitiveMode,
	kSelectBoneMode,
};


/// I3DExSkinable is an interface for a primitive that accepts bones. To create a primitive to
/// which bones can be applied, you just need to derive it from this interface and implement its methods.
///
/// <P>At this point only the vertex primitives support this interface.</P>
/// <P>If you implement the I3DExSkinable interface, it is recommended that your class offers
/// a way to access weights data. For example, you can use the I3DExVertex interface to access
/// the weights on a vertex of a vertex primitive (using SetSkiningData, GetSkiningData).</P>
///
///	<P>The classes supporting I3dExSkinable should provide a low level api to access the skining data.
///	For example, with I3DExVertexPrimitive you can get an I3DExVertex interface for each vertex 
///	and then manipulate the joints weights with SetSkiningData and GetSkiningData.</P>
///
struct I3dExSkinable : public IMCUnknown
{
	///	Attaches the given skeletton, this object and its single instance together for skinning
	///	Note: do NOT call it twice without first dettaching the Skeleton
	///
	///	\param joints					The list of the joints forming the skeleton of the the primitive to.
	///									They MUST have a common root.
	///	\param computeWeights			If true, automatically computes the weights of the joints for each vertex. 
	///									(pass false only when the weights are already there, eg when loading a file)
	///	\param computeJointsTransforms: If true, stores the inverse of the global transform of each joint
	///									if you pass false, you must call SetJointsInverseReferenceTransforms.
	///	\param instance					The single instance of the object
	/// \param undoExtraData			Can be used to undo a detach properly, give the object you get from the detach call
	///
	virtual void		MCCOMAPI AttachSkeleton( TMCPtrArray<I3DShJoint>&	joints, 
												 I3DShInstance*				instance,
												 boolean					computeWeights= true,
												 boolean					computeJointsTransforms= true,
												 IMCUnknown*				undoExtraData=NULL) = 0;

	///	Turns back to a non skinned object.
	///
	virtual void		MCCOMAPI TransferSkeleton(const TMCPtrArray<I3DShJoint>& joints, I3DShInstance* instance) = 0;

	///	Turns back to a non skinned object.
	///
	virtual void		MCCOMAPI DetachAll() = 0;

	///	Detaches the skeleton but keeps the deformers.
	/// undoExtraData can be used to undo a detach properly, give this object to AttachSkeleton
	/// when re-attaching
	///
	virtual void		MCCOMAPI DetachSkeleton(IMCUnknown** undoExtraData=NULL) = 0;

	///	Returns true if the vertex primitive is skinned (skeleton or deformer).
	///
	virtual boolean		MCCOMAPI IsAttached() = 0;

	///	Returns true if the vertex primitive is skinned by a skeleton
	///
	virtual boolean		MCCOMAPI IsSkeletonAttached() = 0;

	///	Returns the joints attached to the vertex object
	///
	/// \param joints Returns the list of the joints that are used for the skinning of this primitive.
	///
	virtual void		MCCOMAPI GetJoints(TMCPtrArray<I3DShJoint>& joints) = 0;	

	///	Returns the inverse global transforms that the joints had when they were attached to the object
	///	The array has the same order and number of elements than GetJoints
	///
	/// \param outTransforms The array of the original inverse transform of the joints.
	///
	virtual void		MCCOMAPI GetJointsInverseReferenceTransforms	(TMCArray<TTransform3D>& outTransforms) = 0;

	///	Sets the inverse global transforms that the joints had when they were attached to the object
	///	The array must have the same order and number of elements than the one given to AttachSkeleton
	///
	/// \param inTransforms The array of the original inverse transform of the joints.
	///
	virtual void		MCCOMAPI SetJointsInverseReferenceTransforms	(const TMCArray<TTransform3D>& inTransforms) = 0;


	///	Called when the transform of a joint change	so that the skinnable can invalidate its caches.
	///
	virtual void		MCCOMAPI InvalidateSkining() = 0;

	///	Returns THE skinned instance.
	///
	/// \param instance A pointer to this instance that is returned.
	///
	virtual void		MCCOMAPI GetInstance(I3DShInstance** instance) = 0;

	///	Returns the global transform of the instance (when the skining was created).
	///
	virtual void		MCCOMAPI GetInstanceGlobalReferenceTransform(TTransform3D& outTransform) = 0;

	///	Sets the global transform of the instance (when loading a file).
	///
	virtual void		MCCOMAPI SetInstanceGlobalReferenceTransform(const TTransform3D& inTransform) = 0;

	/// Called when some weights have been modified.
	///
	virtual void		MCCOMAPI WeightsModified() = 0;

	/// Sets the display mode for this skinable.
	///
	virtual void		MCCOMAPI SetSkinableDisplayMode(ESkinableDisplayMode mode) = 0;

	/// Sets the display mode for this skinable.
	///
	virtual ESkinableDisplayMode	MCCOMAPI GetSkinableDisplayMode() const = 0;

	/// Validates the bounding baxes at the noint level
	/// \see I3DShJoint::
	///
	virtual void		MCCOMAPI ValidateJointBoundingBoxes() = 0;

	/// Sets the skinnable selection mode (see ESkinnableSelectionMode).
	///
	virtual void		MCCOMAPI SetSkinnableSelectionMode(ESkinnableSelectionMode mode) = 0;

	/// Gets the skinnable selection mode (see ESkinnableSelectionMode).
	///
	virtual ESkinnableSelectionMode		MCCOMAPI GetSkinnableSelectionMode() const = 0;

	/// Attaches the given deformer to the skinnable.
	/// \param undoExtraData	Can be used to undo a detach properly, give the object you get from the detach call
	/// to DetachGlobalDeformer.
	/// \param relink			Used if this attach is just a relink (ie. the deformer just has to be attached to
	///							the primitive but all internal parameters already contains this deformer).
	///
	virtual int32		MCCOMAPI AttachGlobalDeformer(I3DShInstance* instance, I3DShGlobalDeformerHelper* deformer, int32 insertIndex=-1, IMCUnknown* undoExtraData=NULL, boolean relink=false) = 0;

	/// Detaches the given deformer.
	/// undoExtraData can be used to undo a detach properly, give this object to AttachGlobalDeformer
	/// when re-attaching
	///
	virtual int32		MCCOMAPI DetachGlobalDeformer(I3DShGlobalDeformerHelper* deformer, IMCUnknown** undoExtraData=NULL) = 0;

	/// Returns all the deformers.
	///
	virtual void		MCCOMAPI GetGlobalDeformers(TMCPtrArray<I3DShGlobalDeformerHelper>& deformers) = 0;

	/// Returns true if the skinnable is deformed by global deformers
	///
	virtual boolean		MCCOMAPI HasGlobalDeformers() const = 0;

	// Detaches all global deformers.
	///
	virtual void		MCCOMAPI DetachAllGlobalDeformers() = 0;

	/// Invalidtaes the given deformer (this function should be called if the deformer instance
	/// is modified, for instance a magnet deformer changes its zone).
	///
	virtual void		MCCOMAPI InvalidateGlobalDeformer(I3DShGlobalDeformerHelper* deformer) = 0;
	
	/// For internal uses
	virtual void		MCCOMAPI SetEditedJointIndex(const TJointIndex& index) = 0;
	/// For internal uses
	virtual TJointIndex MCCOMAPI GetEditedJointIndex() const = 0;
	/// For internal uses
	virtual void		MCCOMAPI SetEditedChannelIndex(uint32 index) = 0;
	/// For internal uses
	virtual uint32		MCCOMAPI GetEditedChannelIndex() = 0;
	///	Returns the Father joint index of the joint index.
	virtual int32		MCCOMAPI GetFatherJointIndex(int32 jointIndex) = 0;	


};

/// \}

#endif