/*
**	Command & Conquer Generals Zero Hour(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : Commando Tools - WWSkin                                      *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Tools/max2w3d/skin.h                         $*
 *                                                                                             *
 *                      $Author:: Moumine_ballo                                               $*
 *                                                                                             *
 *                     $Modtime:: 4/18/01 11:31a                                              $*
 *                                                                                             *
 *                    $Revision:: 8                                                           $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#ifndef SKIN_H
#define SKIN_H

#include <Max.h>
#include "simpmod.h"
#include "simpobj.h"
#include "bpick.h"
#include "namedsel.h"
#include "w3d_file.h"


#define SKIN_OBJ_CLASS_ID		Class_ID(0x32b37e0c, 0x5a9612e4)		
#define SKIN_MOD_CLASS_ID 		Class_ID(0x6bad4898, 0x0d1d6ced)
extern ClassDesc * Get_Skin_Obj_Desc();
extern ClassDesc * Get_Skin_Mod_Desc();


/*

	Writing a space warp plug-in involves creating instances of two key classes.  
	One is derived from class WSMObject.  (WSMObject stands for Word Space Modifier Object,
	just another name for Space Warp Object).  The other class is subclassed off Modifier.
	These two classes work together.  The space warp object handles the display and management 
	of its user interface parameters, the display of the space warp node in the scene, and 
	provides a world space orientation.  The space warp modifier handles the actual deformation
	of the geometry of nodes bound to the space warp.  Each node bound to the space warp
	will have a ModContext which we will store data in.

	The following class is the WSMObject for the westwood skin modifier.

*/
class SkinWSMObjectClass : public SimpleWSMObject, BonePickerUserClass
{	
public:		

	SkinWSMObjectClass();		
	virtual ~SkinWSMObjectClass();

	/*
	** From Animatable		
	*/
	void DeleteThis() { delete this; }		
	void BeginEditParams(IObjParam  *ip, ULONG flags,Animatable *prev);
	void EndEditParams(IObjParam *ip, ULONG flags,Animatable *next);
	TCHAR * GetObjectName() { return _T("WWSkin"); }		
	Class_ID ClassID() { return SKIN_OBJ_CLASS_ID; }		
				
	/*
	** From ReferenceTarget
	*/
	RefTargetHandle Clone(RemapDir& remap = NoRemap());

	/*
	** From Reference Maker.  These three functions give access to the "virtual array" of references.
	** For SkinWSMObjectClass, we have to remember that SimpleWSMObject already has a reference
	** so we are taking ours on after
	*/
	virtual int NumRefs() { return SimpleWSMObject::NumRefs() + Num_Bones();}
	virtual RefTargetHandle GetReference(int i);
	virtual void SetReference(int i, RefTargetHandle rtarg);
	RefResult NotifyRefChanged(Interval changeInt,RefTargetHandle hTarget,PartID& partID, RefMessage message);

	/*
	** From Object		
	*/
	int DoOwnSelectHilite() { return TRUE; }
	CreateMouseCallBack * GetCreateMouseCallBack();

	/*
	** From WSMObject
	*/
	Modifier *CreateWSMMod(INode *node);

	/*
	** From SimpleWSMObject		
	*/
	void BuildMesh(TimeValue t);

	/*
	** Setup a triangle
	*/
	void Build_Tri(Face * f, int a,  int b, int c);
	
	/*
	** Dialog box message processing
	*/
	BOOL SkinWSMObjectClass::Skeleton_Dialog_Proc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
	
	/*
	** Bone picking.
	*/
	virtual void User_Picked_Bone(INode * node);
	virtual void User_Picked_Bones(INodeTab & nodetab);
	void Set_Bone_Selection_Mode(int mode);
	
	int  Add_Bone(INode * node);
	void Add_Bones(INodeTab & nodetab);
	void Remove_Bone(INode * node);
	void Remove_Bones(INodeTab & nodetab);
	void Update_Bone_List(void);

	/*
	** Converting between bone indexes and reference indexes
	** The bone references are a variable number of references which are
	** added at the end of the reference array.  
	*/
	int To_Bone_Index(int refidx) { return refidx - SimpleWSMObject::NumRefs(); }
	int To_Ref_Index(int boneidx) { return SimpleWSMObject::NumRefs() + boneidx; }
	
	/*
	** External access to the bones
	*/
	int Num_Bones(void) { return BoneTab.Count(); }
	INode * Get_Bone(int idx) { return BoneTab[idx]; }
	INodeTab & Get_Bone_List(void) { return BoneTab; }
	int Find_Bone(INode * node);
	int Get_Base_Pose_Frame(void) { return BasePoseFrame; }
	int Get_Base_Pose_Time(void) { return BasePoseFrame * GetTicksPerFrame(); }
	int Find_Closest_Bone(const Point3 & vertex);

	/*
	** Saving and loading.
	*/
	IOResult Save(ISave *isave);
	IOResult Load(ILoad *iload);

	/*
	** Static UI variables.  These have to be static due to the strange way
	** that MAX behaves during creation of objects.  If you create an object
	** then delete it, EndEditParams is not called and its destructor isn't called...
	*/
	static HWND						SotHWND;
	static HWND						SkeletonHWND;
	static HWND						BoneListHWND;
	static IObjParam *			InterfacePtr;
	static ICustButton *			AddBonesButton;
	static ICustButton *			RemoveBonesButton;
	static ISpinnerControl *	BasePoseSpin;

	/*
	** flag for whether we need to build the bones mesh for this object
	*/
	BOOL				MeshBuilt;					

	/*
	** Bone Selection!
	*/
	enum {
		BONE_SEL_MODE_NONE = 0,
		BONE_SEL_MODE_ADD,
		BONE_SEL_MODE_REMOVE,
		BONE_SEL_MODE_ADD_MANY,
		BONE_SEL_MODE_REMOVE_MANY
	};

	int					BoneSelectionMode;
	INodeTab				BoneTab;	

	/*
	** Dialog controls
	*/
	int					BasePoseFrame;

	/*
	** Chunk ID's
	*/
	enum {
		NUM_BONES_CHUNK = 0x0001
	};
	
	/*
	** Friend functions
	*/
	friend BOOL CALLBACK _skeleton_dialog_thunk(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
};

/*
** SkinModifierClass
*/
class SkinModifierClass : public Modifier, BonePickerUserClass
{

public:		

	SkinModifierClass(void);
	SkinModifierClass(INode * node,SkinWSMObjectClass * skin_obj);
#if defined W3D_MAX4		//defined as in the project (.dsp)
	ISubObjType *GetSubObjType(int i);
	int NumSubObjTypes();
#endif
	void								Default_Init(void);

	/*
	** From Animatable
	*/
	void								DeleteThis() { delete this; }
	void								GetClassName(TSTR& s) { s = TSTR(_T("WWSkin")); }
	TCHAR *							GetObjectName() { return _T("WWSkin Binding"); }
	SClass_ID						SuperClassID() { return WSM_CLASS_ID; }		
	Class_ID							ClassID() { return SKIN_MOD_CLASS_ID; } 		
	RefTargetHandle				Clone(RemapDir& remap = NoRemap());
	RefResult						NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message);
	void								BeginEditParams(IObjParam  *ip, ULONG flags,Animatable *prev);
	void								EndEditParams(IObjParam *ip, ULONG flags,Animatable *next);
	CreateMouseCallBack *		GetCreateMouseCallBack() { return NULL; }

	/*
	** From Reference Maker.  These three functions give access to the "virtual array" of references.
	*/
	int								NumRefs() { return 2; }
	RefTargetHandle				GetReference(int i);
	void								SetReference(int i, RefTargetHandle rtarg);

	/*
	** Tell MAX what channels we use and what channels we change:
	** Note that if we do not tell max that we use a channel, that channel is not
	** guaranteed to be valid.
	*/
	virtual ChannelMask ChannelsUsed() { return SELECT_CHANNEL|SUBSEL_TYPE_CHANNEL|GEOM_CHANNEL; }
	virtual ChannelMask ChannelsChanged() { return SELECT_CHANNEL|SUBSEL_TYPE_CHANNEL|GEOM_CHANNEL; }

	/*
	** MAX tells us whenever an input changed.  If we cache anything, we can use this
	** function to dump the cached data and regenerate it.
	*/
	virtual void NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc) {}               

	/*
	** This is where the modifier actually modifies the object!
	*/
	virtual void ModifyObject(TimeValue t, ModContext & mc, ObjectState * os, INode * node);

	/*
	** Since our modifier will be storing information based on the vertex indices, whenever
	** the topology of its input is changed things will no longer work correctly.  Therefore,
	** we tell max that we depend on the topology remaining the same.
	*/
	virtual BOOL DependOnTopology(ModContext &mc) { return TRUE; }

	/*
	** What types of objects can we modify:  The skin modifier will only work with TRIOBJ's
	*/
	virtual Class_ID InputType() { return Class_ID(TRIOBJ_CLASS_ID,0); }

	/*
	** Saving and loading.  Remember to call the base class's save and load functions as well.
	*/
	IOResult Save(ISave *isave);
	IOResult Load(ILoad *iload);
	virtual IOResult LoadLocalData(ILoad *iload, LocalModData **pld);
	virtual IOResult SaveLocalData(ISave *isave, LocalModData *ld);
	
	/*
	** For SkinModifierClass, we allow vertex sub-object selection.
	** This function notifies an object being edited that the current sub object
	** selection level has changed. level==0 indicates object level selection.
	** level==1 or greater refer to the types registered by the object in the
	** order they appeared in the list when registered. If level >= 1, the object
	** should specify sub-object xform modes in the modes structure (defined in cmdmode.h).
	*/
	void ActivateSubobjSel(int level, XFormModes& modes);

	int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt, ModContext* mc);
	void SelectSubComponent(HitRecord *hitRec, BOOL selected, BOOL all, BOOL invert=FALSE);
	void ClearSelection(int selLevel);//
	void SelectAll(int selLevel);
	void InvertSelection(int selLevel);

	/*
	** An object that supports sub-object selection can choose to
	** support named sub object selection sets. Methods in the the
	** interface passed to objects allow them to add items to the
	** sub-object selection set drop down.
	** The following methods implement named sub-obj selection sets 
	*/
	virtual BOOL SupportsNamedSubSels() { return TRUE; }
	virtual void ActivateSubSelSet(TSTR &setName);
	virtual void NewSetFromCurSel(TSTR &setName);
	virtual void RemoveSubSelSet(TSTR &setName);
	void Create_Named_Selection_Sets(void);
	void Install_Named_Selection_Sets(void);

	WSMObject * Get_WSMObject(void) { return (WSMObject*)GetReference(OBJ_REF); }
	Interval Get_Validity(TimeValue t);
	
	/*
	** Bone picking
	*/
	virtual void User_Picked_Bone(INode * node);
	virtual void User_Picked_Bones(INodeTab & nodetab);

	/*
	** Auto-Attach vertices to nearest bone
	*/
	void Auto_Attach_Verts(BOOL all = FALSE);					
	
	/*
	** Unlink selected verts (links them to the root or origin)
	*/
	void Unlink_Verts(void);

private:
	
	/*
	** Windows dialog management and communication functions
	*/	
	void Install_Bone_Influence_Dialog(void);
	void Remove_Bone_Influence_Dialog(void);

	BOOL Bone_Influence_Dialog_Proc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
	
public:

	/*
	** References for SkinModifierClass
	*/
	enum {
		OBJ_REF = 0,
		NODE_REF = 1
	};

	SkinWSMObjectClass *		WSMObjectRef;
	INode *						WSMNodeRef;

	/*
	** Sub-Object Selection variables
	*/
	enum {
		OBJECT_SEL_LEVEL = 0,
		VERTEX_SEL_LEVEL = 1
	};

	int SubObjSelLevel;


	/*
	** Bone Influence Dialog panel variables
	*/
	HWND BoneInfluenceHWND;
	ICustButton *		LinkButton;
	ICustButton *		LinkByNameButton;
	ICustButton *		AutoLinkButton;
	ICustButton *		UnLinkButton;
	
	/*
	**  Cached pointers to some MAX objects
	*/
	IObjParam * InterfacePtr;
	SelectModBoxCMode * SelectMode;		

	/*
	** Load/Save Chunk ID's
	*/
	enum {
		SEL_LEVEL_CHUNK = 0xAA01,
	};

	/*
	** Friend "thunking" functions for the dialog handling.
	*/
	friend BOOL CALLBACK _bone_influence_dialog_thunk(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
};




#endif

