/*
**	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/projectormanager.cpp                  $*
 *                                                                                             *
 *              Original Author:: Greg Hjelstrom                                               *
 *                                                                                             *
 *                      $Author:: Greg_h                                                      $*
 *                                                                                             *
 *                     $Modtime:: 5/14/01 7:58p                                               $*
 *                                                                                             *
 *                    $Revision:: 17                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "projectormanager.h"
#include "colmathaabox.h"
#include "pscene.h"
#include "assetmgr.h"
#include "chunkio.h"
#include "phystexproject.h"
#include "phys.h"
#include "texture.h"


/*
** load up a texture.  also strips off the path since the game has everything
** on one place
*/
TextureClass * create_projector_texture_from_filename( const char * filename )
{
	StringClass	tex_name = filename;
	if (::strchr(tex_name, '\\') != 0) {
		tex_name = ::strrchr(filename, '\\') + 1;
	}

	TextureClass * texture = WW3DAssetManager::Get_Instance()->Get_Texture(tex_name,TextureClass::MIP_LEVELS_1);
	if (texture == NULL) {
		WWDEBUG_SAY(("Failed to create %s from %s\n", (const char *)tex_name, filename));
	}
	return texture;
}


/********************************************************************************************
**
** ProjectorManagerClass Implementation
**
********************************************************************************************/

ProjectorManagerClass::ProjectorManagerClass(void) :
	Projector(NULL),
	ProjectorBoneIndex(0)
{
}

ProjectorManagerClass::~ProjectorManagerClass(void)
{
	Free();
}

void ProjectorManagerClass::Free(void)
{
	if (Projector) {
		PhysicsSceneClass::Get_Instance()->Remove_Texture_Projector(Projector);
		Projector->Release_Ref();
		Projector = NULL;
	}
	ProjectorBoneIndex = 0;
}

void ProjectorManagerClass::Init(const ProjectorManagerDefClass & def,RenderObjClass * model)
{
	Free();
	WWASSERT(Projector == NULL);

	if (model == NULL) {
		return;
	}

	if (def.IsEnabled) {
		
		/*
		** Create the projector
		*/
		Projector = NEW_REF(PhysTexProjectClass, ());
		Projector->Peek_Material_Pass()->Enable_On_Translucent_Meshes(false);

		if (Projector != NULL) {
			/*
			** Set up projection parameters
			*/
			if (def.IsPerspective) {
				Projector->Set_Perspective_Projection(	def.HorizontalFOV,
																	def.VerticalFOV,
																	def.NearZ,
																	def.FarZ);
			} else {
				Projector->Set_Ortho_Projection(			-def.OrthoWidth * 0.5f,def.OrthoWidth * 0.5f,
																	-def.OrthoHeight * 0.5f,def.OrthoHeight * 0.5f,
																	def.NearZ,def.FarZ);
			}

			/*
			** Set up the blending
			*/
			if (def.IsAdditive) {
				Projector->Init_Additive();
			} else {
				Projector->Init_Multiplicative();
			}
			Projector->Set_Intensity(def.Intensity);

			/*
			** Install the texture
			*/
			TextureClass * tex = create_projector_texture_from_filename(def.TextureName);

			if (tex != NULL) {
				tex->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
				tex->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
				Projector->Set_Texture(tex);
				tex->Release_Ref();
			} else {
				Projector->Release_Ref();
				Projector = NULL;
			}

		}

		/*
		** If the projector is animated, resolve the bone name into a bone index and install it into the scene
		** If it is not animated, add it as a static projector (for more efficient culling)
		*/
		if (Projector != NULL) {
			
			/*
			** Find the bone
			*/
			ProjectorBoneIndex = model->Get_Bone_Index(def.BoneName);
			if (ProjectorBoneIndex == 0xFFFF) {
				ProjectorBoneIndex = 0;
			}
			Projector->Set_Transform(model->Get_Bone_Transform(ProjectorBoneIndex));

			Set_Flag(IS_ANIMATED,(def.IsAnimated) && (ProjectorBoneIndex != 0));
			
			if (Get_Flag(IS_ANIMATED)) {
				PhysicsSceneClass::Get_Instance()->Add_Dynamic_Texture_Projector(Projector);
			} else {
				PhysicsSceneClass::Get_Instance()->Add_Static_Texture_Projector(Projector);
			}
		}
	}
}

void ProjectorManagerClass::Update_From_Model(RenderObjClass * model)
{
	if ((Projector != NULL) && (ProjectorBoneIndex != -1)) {
		const Matrix3D & tm = model->Get_Bone_Transform(ProjectorBoneIndex);
		Projector->Set_Transform(tm);
	}
}


/********************************************************************************************
**
** ProjectorManagerDefClass Implementation
** Note, ProjectorManagerDef is not a full-fleged definition class.  It is meant to be
** embedded inside another real definition.
**
********************************************************************************************/

enum 
{
	PROJECTORMANAGERDEF_CHUNK_VARIABLES							= 0x01110004,
	
	PROJECTORMANAGERDEF_VARIABLE_ISENABLED						= 0x00,
	PROJECTORMANAGERDEF_VARIABLE_ISPERSPECTIVE,
	PROJECTORMANAGERDEF_VARIABLE_ISADDITIVE,
	PROJECTORMANAGERDEF_VARIABLE_ISANIMATED,
	PROJECTORMANAGERDEF_VARIABLE_ORTHOWIDTH,
	PROJECTORMANAGERDEF_VARIABLE_ORTHOHEIGHT,
	PROJECTORMANAGERDEF_VARIABLE_HORIZONTALFOV,
	PROJECTORMANAGERDEF_VARIABLE_VERTICALFOV,
	PROJECTORMANAGERDEF_VARIABLE_NEARZ,
	PROJECTORMANAGERDEF_VARIABLE_FARZ,
	PROJECTORMANAGERDEF_VARIABLE_TEXTURENAME,
	PROJECTORMANAGERDEF_VARIABLE_BONENAME,

	PROJECTORMANAGERDEF_VARIABLE_INTENSITY,
};


ProjectorManagerDefClass::ProjectorManagerDefClass(void) :
	IsEnabled(false),
	IsPerspective(false),
	IsAdditive(false),
	IsAnimated(false),
	OrthoWidth(10.0f),
	OrthoHeight(10.0f),
	HorizontalFOV(DEG_TO_RADF(10.0f)),
	VerticalFOV(DEG_TO_RADF(10.0f)),
	NearZ(5.0f),
	FarZ(20.0f),
	Intensity(1.0f)
{
}

ProjectorManagerDefClass::~ProjectorManagerDefClass(void)
{
}

void ProjectorManagerDefClass::Validate_Parameters(void)
{
	if (HorizontalFOV <= 0.0f) { HorizontalFOV = DEG_TO_RADF(10.0f); }
	if (VerticalFOV <= 0.0f) { VerticalFOV = DEG_TO_RADF(10.0f); }
	if (OrthoWidth <= 0.0f) { OrthoWidth = 10.0f; }
	if (OrthoHeight <= 0.0f) { OrthoHeight = 10.0f; }
	if (NearZ < 0.0f) { NearZ = 0.0f; }
	if (FarZ < NearZ) { FarZ = NearZ + 10.0f; }
}

bool ProjectorManagerDefClass::Save(ChunkSaveClass &csave)
{
	Validate_Parameters();

	csave.Begin_Chunk(PROJECTORMANAGERDEF_CHUNK_VARIABLES);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_ISENABLED,IsEnabled);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_ISPERSPECTIVE,IsPerspective);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_ISADDITIVE,IsAdditive);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_ISANIMATED,IsAnimated);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_ORTHOWIDTH,OrthoWidth);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_ORTHOHEIGHT,OrthoHeight);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_HORIZONTALFOV,HorizontalFOV);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_VERTICALFOV,VerticalFOV);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_NEARZ,NearZ);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_FARZ,FarZ);
	WRITE_MICRO_CHUNK_WWSTRING(csave,PROJECTORMANAGERDEF_VARIABLE_TEXTURENAME,TextureName);
	WRITE_MICRO_CHUNK_WWSTRING(csave,PROJECTORMANAGERDEF_VARIABLE_BONENAME,BoneName);
	WRITE_MICRO_CHUNK(csave,PROJECTORMANAGERDEF_VARIABLE_INTENSITY,Intensity);
	csave.End_Chunk();

	return true;
}

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

		switch(cload.Cur_Chunk_ID()) {

			case PROJECTORMANAGERDEF_CHUNK_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_ISENABLED,IsEnabled);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_ISPERSPECTIVE,IsPerspective);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_ISADDITIVE,IsAdditive);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_ISANIMATED,IsAnimated);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_ORTHOWIDTH,OrthoWidth);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_ORTHOHEIGHT,OrthoHeight);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_HORIZONTALFOV,HorizontalFOV);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_VERTICALFOV,VerticalFOV);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_NEARZ,NearZ);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_FARZ,FarZ);
						READ_MICRO_CHUNK_WWSTRING(cload,PROJECTORMANAGERDEF_VARIABLE_TEXTURENAME,TextureName);
						READ_MICRO_CHUNK_WWSTRING(cload,PROJECTORMANAGERDEF_VARIABLE_BONENAME,BoneName);
						READ_MICRO_CHUNK(cload,PROJECTORMANAGERDEF_VARIABLE_INTENSITY,Intensity);
					}
					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;
}
