/*
**	Command & Conquer Generals(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 : Max2W3d                                                      *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Tools/max2w3d/gridsnapmodifier.cpp           $*
 *                                                                                             *
 *              Original Author:: Greg Hjelstrom                                               *
 *                                                                                             *
 *                      $Author:: Greg_h                                                      $*
 *                                                                                             *
 *                     $Modtime:: 5/01/01 8:29p                                               $*
 *                                                                                             *
 *                    $Revision:: 1                                                           $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */



#include "Max.h"
#include "resource.h"
#include "simpmod.h"
#include "dllmain.h"
#include "iparamb2.h"


/*

  WARNING WARNING WARNING PLEASE READ - This modifier was an experiment to see if we could
  solve cracks in adjacent meshes by snapping to a world-space grid.  It didn't work for a
  few reasons:
  - I couldn't implement the world space snapping; the SimpleMod code seems to always force
  you to work relative to each object.
  - Snapping to a grid won't always snap vertices to the same grid.  The probability that
  it will work is a function of the distance between the points and the grid spacing

*/



#define GRIDSNAPMOD_CLASSID Class_ID(0x7a2d399b, 0x1e3d2004)


/**
** GridSnapModifierClass
** This modifier will snap all vertices in the geometry being modified to a grid.  Its motivation is to 
** try to help solve the problem of cracks between adjacent meshes in Renegade levels.  This will work
** a lot better if the objects have reset-transforms prior to being processed by this modifier.
*/
class GridSnapModifierClass : public SimpleMod2 
{	
public:

	GridSnapModifierClass();

	// From Animatable
	void DeleteThis()																			{ delete this; }
	void GetClassName(TSTR& s)																{ s = Get_String(IDS_GRIDSNAPMODIFIER); }  
	virtual Class_ID ClassID()																{ return GRIDSNAPMOD_CLASSID; }		
	void BeginEditParams( IObjParam  *ip, ULONG flags,Animatable *prev);
	void EndEditParams( IObjParam *ip,ULONG flags,Animatable *next);
	RefTargetHandle Clone(RemapDir& remap = NoRemap());
	TCHAR *GetObjectName()																	{ return Get_String(IDS_GRIDSNAPMODIFIER);}
	IOResult Load(ILoad *iload);

	// Direct paramblock access
	int	NumParamBlocks()																	{ return 1; }	
	IParamBlock2* GetParamBlock(int i)													{ return pblock2; }
	IParamBlock2* GetParamBlockByID(BlockID id)										{ return (pblock2->ID() == id) ? pblock2 : NULL; }

	// From simple mod
	Deformer& GetDeformer(TimeValue t,ModContext &mc,Matrix3& mat,Matrix3& invmat);		
	Interval GetValidity(TimeValue t);

	//RefTargetHandle GetReference(int i)
	//void SetReference(int i,RefTargetHandle rtar)
	//Animatable * SubAnim(int i)
};

/**
** GridSnapDeformerClass
** This is the callback object used by GridSnapModifierClass to implement the actual geometry changes.  This
** architecture is required by the SimpleMod base-class.  This Deformer simply snaps vertex positions
** to the grid defined by its parameters.
*/
class GridSnapDeformerClass : public Deformer
{
public:
	GridSnapDeformerClass(void) : GridDimension(0.001f) {}
		
	void		Set_Grid_Dimension(float grid_dim)		{ GridDimension = grid_dim; }
	float		Get_Grid_Dimension(void)					{ return GridDimension; }
	
	void		Set_Matrices(const Matrix3 & tm,const Matrix3 & invtm)	{ Transform = tm; InvTransform = invtm; }

	virtual Point3 Map(int i,Point3 p)
	{
		p = p*Transform;
		p.x = floor(p.x / GridDimension) * GridDimension;
		p.y = floor(p.y / GridDimension) * GridDimension;
		p.z = floor(p.z / GridDimension) * GridDimension;
		p = p*InvTransform;

		return p;
	}

private:
	float		GridDimension;
	Matrix3	Transform;
	Matrix3	InvTransform;
};


/** 
** GridSnapModifier Class Descriptor
** This object "links" the plugin into Max's plugin system.  It links the Class-ID to a virtual construction
** method.  The function Get_Grid_Snap_Modifier_Desc is the only hook to external code.
*/
class GridSnapModifierClassDesc:public ClassDesc2 
{
public:
	int 				IsPublic()											{ return 1; }
	void *			Create(BOOL loading = FALSE)					{ return new GridSnapModifierClass; }
	const TCHAR *	ClassName()											{ return _T("Grid Snap Modifier"); }
	SClass_ID		SuperClassID()										{ return OSM_CLASS_ID; }
	Class_ID			ClassID()											{ return GRIDSNAPMOD_CLASSID; }
	const TCHAR* 	Category()											{ return _T("Westwood Modifiers");}
	HINSTANCE		HInstance()											{ return AppInstance; }
	const TCHAR *	InternalName()										{ return _T("Westwood GridSnap"); }
};

static GridSnapModifierClassDesc _GridSnapModifierDesc;

ClassDesc* Get_Grid_Snap_Modifier_Desc(void) 
{	
	return &_GridSnapModifierDesc; 
}


/*
** ParamBlock2 Setup
*/
enum 
{
	GSM_PARAMS = 0,
};

enum 
{
	GSM_PARAM_GRIDDIMENSION = 0,	
};

static ParamBlockDesc2 _GridSnapParamBlockDesc
(
	// parameter block settings
	GSM_PARAMS,_T("GridSnap Parameters"), 0, &_GridSnapModifierDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, SIMPMOD_PBLOCKREF,
	
	// dialog box
	IDD_GRIDSNAP_PARAMS, IDS_GRIDSNAP_TITLE, 0, 0, NULL,
	
	// parameters
	GSM_PARAM_GRIDDIMENSION, _T("Grid Dimension"), TYPE_FLOAT, P_RESET_DEFAULT, IDS_GRID_DIMENSION,
		p_default,		0.001f,
		p_range,			0.0001f, 10.0f,
		p_ui,				TYPE_SPINNER, EDITTYPE_FLOAT, IDC_GRIDDIM_EDIT, IDC_GRIDDIM_SPIN, 0.0001f,
		end,

	end
);


/********************************************************************************************
**
** GridSnapModifierClass Implementation
**
********************************************************************************************/

GridSnapModifierClass::GridSnapModifierClass()
{
	// create the parameter block, storing in the base-class's pblock variable
	_GridSnapModifierDesc.MakeAutoParamBlocks(this);
	assert(pblock2);
}

void GridSnapModifierClass::BeginEditParams( IObjParam  *ip, ULONG flags,Animatable *prev)
{
	this->ip = ip;

	SimpleMod2::BeginEditParams(ip,flags,prev);
	_GridSnapModifierDesc.BeginEditParams(ip, this, flags, prev);
}

void GridSnapModifierClass::EndEditParams( IObjParam *ip,ULONG flags,Animatable *next)
{
	SimpleMod2::EndEditParams(ip,flags,next);
	_GridSnapModifierDesc.EndEditParams(ip, this, flags, next);

	this->ip = NULL;
}

RefTargetHandle GridSnapModifierClass::Clone(RemapDir& remap)
{
	GridSnapModifierClass * newmod = new GridSnapModifierClass();	
	newmod->ReplaceReference(SIMPMOD_PBLOCKREF,pblock2->Clone(remap));
	newmod->SimpleModClone(this);
	return(newmod);
}

IOResult GridSnapModifierClass::Load(ILoad *iload)
{
	Modifier::Load(iload);
	return IO_OK;
}

Deformer& GridSnapModifierClass::GetDeformer(TimeValue t,ModContext &mc,Matrix3& mat,Matrix3& invmat)
{
	float dimension = 0.0f; Interval valid = FOREVER;
	pblock2->GetValue(GSM_PARAM_GRIDDIMENSION, t, dimension, FOREVER);

	static GridSnapDeformerClass deformer;
	deformer.Set_Grid_Dimension(dimension);
	deformer.Set_Matrices(mat,invmat);
	return deformer;
}

Interval GridSnapModifierClass::GetValidity(TimeValue t)
{
	float f;	
	Interval valid = FOREVER;
	pblock2->GetValue(GSM_PARAM_GRIDDIMENSION, t, f, valid);
	return valid;
}

RefTargetHandle SimpleMod2::GetReference(int i)
{
	switch (i) {
		case 0: return tmControl;
		case 1: return posControl;
		case 2: return pblock2;
		default: return NULL;
	}
}

void SimpleMod2::SetReference(int i,RefTargetHandle rtarg)
{
	switch (i) {
		case 0: tmControl = (Control*)rtarg; break;
		case 1: posControl = (Control*)rtarg; break;
		case 2: pblock2 = (IParamBlock2*)rtarg; break;
	}
}

Animatable * SimpleMod2::SubAnim(int i)
{
	switch (i) {
		case 0: return posControl;
		case 1: return tmControl;
		case 2: return pblock2;		
		default: return NULL;
	}
}