/*
**	Command & Conquer Renegade(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 : WWPhys                                                       *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/wwphys/dynamicanimphys.cpp                   $*
 *                                                                                             *
 *              Original Author:: Greg Hjelstrom                                               *
 *                                                                                             *
 *                      $Author:: Greg_h                                                      $*
 *                                                                                             *
 *                     $Modtime:: 11/01/01 2:41p                                              $*
 *                                                                                             *
 *                    $Revision:: 12                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "dynamicanimphys.h"
#include "wwphysids.h"
#include "persistfactory.h"
#include "simpledefinitionfactory.h"
#include "wwhack.h"
#include "wwprofile.h"
#include "matinfo.h"



DECLARE_FORCE_LINK(dynamicanimphys);


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

	DynamicAnimPhysClass Implementation

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

/*
** Declare a PersistFactory for DynamicAnimPhysClass.  This enables the class to save and load.
*/
SimplePersistFactoryClass<DynamicAnimPhysClass,PHYSICS_CHUNKID_DYNAMICANIMPHYS>	_DynamicAnimPhysFactory;

/*
** Chunk ID's used by DynamicAnimPhysClass
*/
enum
{
	DYNAMICANIMPHYS_CHUNK_DECOPHYS							= 526000339,
	DYNAMICANIMPHYS_CHUNK_VARIABLES,
	DYNAMICANIMPHYS_CHUNK_ANIMMANAGER,
};										


DynamicAnimPhysClass::DynamicAnimPhysClass(void) :
	AnimManager(*this),
	ShadowManager(*this)
{
}

DynamicAnimPhysClass::~DynamicAnimPhysClass(void)
{
}

void DynamicAnimPhysClass::Init(const DynamicAnimPhysDefClass & def)
{
	DecorationPhysClass::Init(def);
	AnimManager.Init(def.AnimManagerDef);
}

void DynamicAnimPhysClass::Set_Model(RenderObjClass * model)
{
	DecorationPhysClass::Set_Model(model);
	Update_Cached_Model_Parameters();

	/*
	** Nuke strike uses a linear offset mapper to fade out, have to reset it so I'm
	** just resetting any mappers in any model used by a DynamicAnimPhys... hacky, oh well.
	*/
	if (Model != NULL) {
		Reset_Mappers(Model);
	}
}

void DynamicAnimPhysClass::Update_Cached_Model_Parameters(void)
{
	/*
	** Set up our animation manager
	*/
	AnimManager.Update_Cached_Model_Parameters();

	/*
	** Set up our shadow manager
	*/
	const DynamicAnimPhysDefClass * def = Get_DynamicAnimPhysDef();
	Enable_Shadow_Generation(def->CastsShadows);
	ShadowManager.Set_Shadow_Planes(def->ShadowNearZ,def->ShadowFarZ);
}

void DynamicAnimPhysClass::Reset_Mappers(RenderObjClass * model)
{
	if (model != NULL) {
		
		MaterialInfoClass * matinfo = model->Get_Material_Info();
		if (matinfo != NULL) {
			matinfo->Reset_Texture_Mappers();
		}
		REF_PTR_RELEASE(matinfo);
		
		for (int i=0; i<model->Get_Num_Sub_Objects(); i++) {
			RenderObjClass * sub_obj = model->Get_Sub_Object(i);
			Reset_Mappers(sub_obj);
			REF_PTR_RELEASE(sub_obj);
		}
	}
}

void DynamicAnimPhysClass::Timestep(float dt)
{
	WWPROFILE("DynAnimPhys::Timestep");

	/*
	** Let the animation manager handle progressing the animation, checking
	** for collisions, and managing any "riders"
	*/
	bool anim_changed = AnimManager.Timestep(dt);
	if (anim_changed) {
		Update_Visibility_Status();
		Update_Cull_Box();
	}
}

void DynamicAnimPhysClass::Post_Timestep_Process(void)
{
	ShadowManager.Update_Shadow();
}

const PersistFactoryClass & DynamicAnimPhysClass::Get_Factory(void) const
{
	return _DynamicAnimPhysFactory;
}

bool DynamicAnimPhysClass::Save(ChunkSaveClass &csave)
{
	csave.Begin_Chunk(DYNAMICANIMPHYS_CHUNK_DECOPHYS);
	DecorationPhysClass::Save(csave);
	csave.End_Chunk();

	csave.Begin_Chunk(DYNAMICANIMPHYS_CHUNK_ANIMMANAGER);
	AnimManager.Save(csave);
	csave.End_Chunk();
	
	return true;
}

bool DynamicAnimPhysClass::Load(ChunkLoadClass &cload)
{
	/*
	** Read in the chunks from the file
	*/
	while (cload.Open_Chunk()) {
		
		switch(cload.Cur_Chunk_ID()) 
		{
			case DYNAMICANIMPHYS_CHUNK_DECOPHYS:
				DecorationPhysClass::Load(cload);
				break;

			case DYNAMICANIMPHYS_CHUNK_ANIMMANAGER:
				AnimManager.Load(cload);
				break;

			default:
				WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
				break;
		}
		
		cload.Close_Chunk();
	}

	SaveLoadSystemClass::Register_Post_Load_Callback(this);
	return true;
}

void DynamicAnimPhysClass::On_Post_Load(void)
{
	DecorationPhysClass::On_Post_Load();

	/*
	** Always re-initialize any variables which depend on the model
	*/
	Update_Cached_Model_Parameters();

}


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

	DynamicAnimPhysDefClass Implementation

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

/*
** Persist factory for StaticAnimPhysDefClass's.  This makes them able to save and load.
*/
SimplePersistFactoryClass<DynamicAnimPhysDefClass,PHYSICS_CHUNKID_DYNAMICANIMPHYSDEF>	_DynamicAnimPhysDefFactory;

/*
** Definition factory for StaticAnimPhysDefClass.  This makes it show up in the editor
*/
DECLARE_DEFINITION_FACTORY(DynamicAnimPhysDefClass, CLASSID_DYNAMICANIMPHYSDEF, "DynamicAnimPhys") _DynamicAnimPhysDefDefFactory;

/*
** Chunk ID's used by StaticAnimPhysDefClass
*/
enum 
{
	DYNAMICANIMPHYSDEF_CHUNK_DECOPHYSDEF				= 052600316,			// (parent class)
	DYNAMICANIMPHYSDEF_CHUNK_ANIMMANAGERDEF,
	DYNAMICANIMPHYSDEF_CHUNK_VARIABLES,

	DYNAMICANIMPHYSDEF_VARIABLE_CASTSSHADOWS			= 0x01,
	DYNAMICANIMPHYSDEF_VARIABLE_SHADOWNEARZ,
	DYNAMICANIMPHYSDEF_VARIABLE_SHADOWFARZ,
};



DynamicAnimPhysDefClass::DynamicAnimPhysDefClass(void) :
	CastsShadows(false),
	ShadowNearZ(-1.0f),
	ShadowFarZ(-1.0f)
{
	// Make the animation manager variables editable
	ANIMCOLLISIONMANAGERDEF_EDITABLE_PARAMS( DynamicAnimPhysDefClass , AnimManagerDef );
  	PARAM_SEPARATOR(DynamicAnimPhysDefClass, "Shadow Settings");
	EDITABLE_PARAM(DynamicAnimPhysDefClass,ParameterClass::TYPE_BOOL, CastsShadows);
	FLOAT_UNITS_PARAM(DynamicAnimPhysDefClass,ShadowNearZ, -1.0f,1000.0f, "meters (-1 for default)") 
	FLOAT_UNITS_PARAM(DynamicAnimPhysDefClass,ShadowFarZ, -1.0f,1000.0f, "meters (-1 for default)") 
}

uint32 DynamicAnimPhysDefClass::Get_Class_ID (void) const	
{ 
	return CLASSID_DYNAMICANIMPHYSDEF; 
}

PersistClass * DynamicAnimPhysDefClass::Create(void) const
{
	DynamicAnimPhysClass * obj = NEW_REF(DynamicAnimPhysClass,());
	obj->Init(*this);
	return obj;
}

const PersistFactoryClass & DynamicAnimPhysDefClass::Get_Factory (void) const
{
	return _DynamicAnimPhysDefFactory;
}

const char * DynamicAnimPhysDefClass::Get_Type_Name(void)
{ 
	return "DynamicAnimPhysDef"; 
}

bool DynamicAnimPhysDefClass::Is_Type(const char * type_name)
{
	if (stricmp(type_name,DynamicAnimPhysDefClass::Get_Type_Name()) == 0) {
		return true;
	} else {
		return DecorationPhysDefClass::Is_Type(type_name);
	}
}


bool DynamicAnimPhysDefClass::Save(ChunkSaveClass &csave)
{
	csave.Begin_Chunk(DYNAMICANIMPHYSDEF_CHUNK_DECOPHYSDEF);
	DecorationPhysDefClass::Save(csave);
	csave.End_Chunk();

	csave.Begin_Chunk(DYNAMICANIMPHYSDEF_CHUNK_ANIMMANAGERDEF);
	AnimManagerDef.Save(csave);
	csave.End_Chunk();

	csave.Begin_Chunk(DYNAMICANIMPHYSDEF_CHUNK_VARIABLES);
	WRITE_MICRO_CHUNK(csave,DYNAMICANIMPHYSDEF_VARIABLE_CASTSSHADOWS,CastsShadows);
	WRITE_MICRO_CHUNK(csave,DYNAMICANIMPHYSDEF_VARIABLE_SHADOWNEARZ,ShadowNearZ);
	WRITE_MICRO_CHUNK(csave,DYNAMICANIMPHYSDEF_VARIABLE_SHADOWFARZ,ShadowFarZ);
	csave.End_Chunk();

	return true;
}

bool DynamicAnimPhysDefClass::Load(ChunkLoadClass &cload)
{
	while (cload.Open_Chunk()) {

		switch(cload.Cur_Chunk_ID()) {

			case DYNAMICANIMPHYSDEF_CHUNK_DECOPHYSDEF:
				DecorationPhysDefClass::Load(cload);
				break;

			case DYNAMICANIMPHYSDEF_CHUNK_ANIMMANAGERDEF:
				AnimManagerDef.Load(cload);
				break;

			case DYNAMICANIMPHYSDEF_CHUNK_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK(cload,DYNAMICANIMPHYSDEF_VARIABLE_CASTSSHADOWS,CastsShadows);
						READ_MICRO_CHUNK(cload,DYNAMICANIMPHYSDEF_VARIABLE_SHADOWNEARZ,ShadowNearZ);
						READ_MICRO_CHUNK(cload,DYNAMICANIMPHYSDEF_VARIABLE_SHADOWFARZ,ShadowFarZ);
					}
					cload.Close_Micro_Chunk();	
				}
				break;

			default:
				WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
				break;
		}

		cload.Close_Chunk();
	}
	return true;
}

