//===========================================================================//
// File:	ArmatureExp.cpp                                                 
// Project: MechWarrior 4
// Contents: Plugin to adjust height of max anmiations
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 04/28/99  TAS  Initial coding, 
//---------------------------------------------------------------------------//
// Copyright (C) 1999, Fasa Interactive, Inc.
// All Rights reserved worldwide
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL
//===========================================================================//


#include "stdafx.h"

#include <GameOS\GameOS.hpp>
#include <GameOs\TooLOS.hpp>
#include "MaxProxies\MaxProxyHeaders.hpp"
#include "Proxies\ProxyHeaders.hpp"
#include <proxies\optimizeflatshading.hpp>
#include "ExportUtil.h"

#if !defined(DLLPLATFORM_DLLPLATFORM_HPP)
#include "DLLPlatform\DLLPlatform.hpp"
#endif

#ifdef STRICT							// Already defined by stdafx,
#undef STRICT							// so we avoid warning linking msg
#endif
#ifdef _MBCS							// The same as above
#undef _MBCS
#endif

#include "ArmatureExport.h"


//undef Verify

#ifndef _DEBUG						//This is a hack To make it link in Release
#undef Spew
//void Spew() {}
#endif


//===========================================================================//

int	ArmatureExp::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts)
{	
    int return_value;
    __try
    {
        return_value = m_Expt.DoExportWrap(name, ei, i, suppressPrompts);
        //return_value = true;
        GetSystemSetting(SYSSET_CLEAR_UNDO);
        Verify(!theHold.Holding());
    }
    __except(ProcessException(GetExceptionInformation()))
    {
    }
    
    return return_value;
}


//===========================================================================//

int	ArmatureExporter::DoExportWrap(const TCHAR *name,ExpInterface *ei,Interface *ip, BOOL suppressPrompts,bool firstwrite)
{
    
    m_first_write=firstwrite;	
    
	StartExport(ip, CString("Exporting Armature data."), suppressPrompts?true:false);

    MAXProxies::MAXScene *max_scene = MakeMaxScene(ip,m_strHintFile);
   
	//
	// fill in all the directories
	//
	if (!LoadPlugCfg(ip))
		return false;
    
	// Check for errors   
    if (!CheckForErrors(max_scene,ip))
    {
        return false;
    }

	// Load settings    
    m_exportInfo.exportType = EXPORT_CHARACTER;
    m_exportInfo.outputAmatureFile = true;
    m_exportInfo.overwriteTextures = false;    
    SaveExportInfo(max_scene->GetProxiedScene());
    
	//
    // set up some name and path stuff
    //
    TSTR filename, extension;
    TSTR fullpath = _T(name);
    TSTR directoryPath;
    SplitFilename(fullpath,&directoryPath,&filename,&extension);
    directoryPath += "\\";
    
    //
    // write out the armarture file and content file
    //
	WriteOutArmatureFile(CString(directoryPath),CString(filename),max_scene->GetProxiedScene());
	WriteOutContentFile(CString(directoryPath),CString(filename),max_scene->GetProxiedScene());
	WriteOutGeometry_Video_Data_Files(CString(directoryPath), max_scene->GetProxiedScene());
    
	EndExport(ip);

    return true;        
}

/*******************************************************************************
/* function name: WriteOutContentFile
/* description: 
/*******************************************************************************/
int ArmatureExporter::WriteOutContentFile(const CString& directoryPath, const CString& filename, INode *node)
{
    Check_Pointer(Stuff::FileStreamManager::Instance);

    CString content_filename = directoryPath; 
    content_filename += filename;
    content_filename += ".contents";
    //if ( gos_DoesFileExist(content_filename) ) return 0;
    
    Stuff::NotationFile content_file;
    
    CString armature_filename ; 
    armature_filename += filename;
    armature_filename += ".armature";
    
    Page *page = content_file.SetPage("Includes");
    Check_Object(page);
    page->AppendEntry("!include",armature_filename);
    
    TraverseTreeContent(node,&content_file);
    content_file.SaveAs(content_filename);
    return 0;
}

/*******************************************************************************
/* function name: TraverseTreeContent
/* description: 
/*******************************************************************************/
int ArmatureExporter::TraverseTreeContent(INode *node, Stuff::NotationFile *content_file)
{
    for (int c = 0; c < node->NumberOfChildren(); c++) {
        TraverseTreeContent(node->GetChildNode(c),content_file);
        
        INode *child = node->GetChildNode(c);
        CString name=child->GetName();
		name.MakeLower();
        name.Replace(' ', '_');

        CString prefix =name.Left(name.Find("_",0));
        
        if (!IsNodeCamera(child))
        {
            if (!(IsNodeGeometry(child)) &&  (!prefix.CompareNoCase("joint") || !prefix.CompareNoCase("site")))
            {
				Page *page = content_file->SetPage(name);
                Check_Object(page);

                if(name.Right(5).CompareNoCase("ankle")==NULL)
                {
                    page->SetEntry("Model","basic.data");
                    page->AppendEntry("ExecutionState","AlwaysExecuteState");
                    
                    CString cname=name.Left(name.GetLength()-5)+"below"+name.Right(5);
                    page = content_file->SetPage(cname);
                    page->SetEntry("Model","basic.data");
                    page->AppendEntry("ExecutionState","AlwaysExecuteState");
                    name=cname;
                    page = content_file->GetPage(name);
                }
				else if (name.Right(4).CompareNoCase("cage")==NULL)
				{
					page->SetEntry("Model","armaturedata\\joint_cage.data");
                    page->AppendEntry("ExecutionState","AlwaysExecuteState");
				}
                else
                {
                    page->SetEntry("Model","basic.data");
                    page->AppendEntry("ExecutionState","AlwaysExecuteState");
                }
				
				//
				// now write out the list of children
				//
				
				CString data_filename  = "basic.data";
				int number_children = child->NumberOfChildren();        
				
				for (int y=0; y < number_children; y++)
				{
					INode*  sib = child->GetChildNode(y);
					if (sib)
					{
						CString sib_name = sib->GetName();
						sib_name.Replace(' ', '_');
						if (IsNodeGeometry(sib) && !IsNodePartOfGroupGeometry(sib))
						{
							data_filename  = "armaturedata\\";
							data_filename += sib_name;
							data_filename += ".data";
							break;
						}
						else if (y == child->NumberOfChildren()-1)
						{
							data_filename  = "basic.data";
						}
					}
				}
				
				if (name.Find("joint_cage")>=0)
					data_filename  = "armaturedata\\joint_cage.data";

				for (int z=0; z < number_children; z++)
				{
					INode*  sib = child->GetChildNode(z);
					if (sib)
					{
						if ( !prefix.CompareNoCase("joint") || !prefix.CompareNoCase("site"))
							page->SetEntry("Model",data_filename);
					}
				}
			}
		}
	}
    return 0;
}

/*******************************************************************************
/* function name: WriteOutGeometry_Video_Data_Files
/* description: Write out armaturedata\.data files and armaturevideo\*.video files
/*	for all the geometries in a skeleton max file
/*******************************************************************************/
int ArmatureExporter::WriteOutGeometry_Video_Data_Files(
	const CString& directoryPath, INode *node, const CString& strCage, bool bSetUniqueYes)
{
    
    for (int c = 0; c < node->NumberOfChildren(); c++) 
    {
        WriteOutGeometry_Video_Data_Files(directoryPath, node->GetChildNode(c), strCage, bSetUniqueYes);
        
        INode *child = node->GetChildNode(c);
        CString name = child->GetName();
		name.MakeLower();
        name.Replace(' ', '_');

        if ((!IsNodeCamera(child) && IsNodeGeometry(child) && !IsNodePartOfGroupGeometry(child)) ||
			name.Find("joint_cage")>=0)
        {
                                    
            CString data_filename = directoryPath;
            data_filename += "armaturedata\\";
            gos_CreateDirectory( data_filename );
            data_filename += name;
            data_filename += ".data";
            
            CString video_holder_name = "armaturevideo\\";
            video_holder_name += name;
            video_holder_name += ".video";

            //
            // make the stream for the data file and create the file
            //
            Stuff::NotationFile data_file;
            Page *page = data_file.SetPage("GameData");
            Check_Object(page);
            page->SetEntry("Class","MechWarrior4::MWMover");
            page = data_file.SetPage("Renderers");
            Check_Object(page);
            page->SetEntry("VideoRenderer",video_holder_name);
            data_file.SaveAs(data_filename);

			// Get the light erf's name
			TurnOffExceptions();

			INode *lightNode = GetLightNode(child);
			CString lightFileName = "";
			if (lightNode) {
				CString lightName = lightNode->GetName();
				lightName.MakeLower();
		        lightName.Replace(' ', '_');
				lightFileName = lightName + ".erf";
			}

			TurnOnExceptions();

			// Don't write out .video files for lights and KEEP_ nodes
			if (name.Find("light", 0)<0 && name.Find("keep_", 0)<0) {
			    CString video_filepath = directoryPath;
				video_filepath += "armaturevideo\\";
				gos_CreateDirectory( video_filepath );

				GeoExporter exp(NULL);

				// Get the root erf's names
				RootGeo rootGeos[MAX_PATH];
				int numOfRootGeos = 1;
				rootGeos[0].name=name;
				rootGeos[0].type="ShapeComponent";

				// Get all the geometries which are going into this video file
				// and call GeoExporter's method to do the work
				exp.GetRootGeos(directoryPath, rootGeos, &numOfRootGeos, node);
				exp.WriteOutGeometry_Video_Data_Files(
					directoryPath, "armaturevideo\\"+ name, strCage, 
					lightFileName, rootGeos, numOfRootGeos, bSetUniqueYes);		
			}
		}
    }
    
    return 0;    
}

/*******************************************************************************
/* function name: WriteOutArmatureFile
/* description: 
/*******************************************************************************/
int ArmatureExporter::WriteOutArmatureFile(const CString& directoryPath, const CString& filename, INode *node)
{
    CString armature_filename = directoryPath; 
    armature_filename += filename;
    armature_filename += ".armature";
    
    Check_Pointer(Stuff::FileStreamManager::Instance);
    
    Stuff::NotationFile armature_file;
    TraverseTree(node,&armature_file);

    Page *page = armature_file.SetPage("");
    Check_Object(page);
    page->AppendEntry("!concatenate", "true");
    
	armature_file.SaveAs(armature_filename);
    
    return 0;
}

/*******************************************************************************
/* function name: GetLocalToParent
/* description: 
/*******************************************************************************/
bool ArmatureExporter::GetLocalToParent(INode *node, Stuff::LinearMatrix4D *matrix)
{
    Check_Pointer(matrix);
    
    Matrix3 myTM = node->GetNodeTM(0,NULL);
    Matrix3 parentTM = node->GetParentTM(0);
    // Matrix3 objectTM = node->GetObjectTM(0,NULL);
    Matrix3 mat = myTM * Inverse(parentTM);
    //
    // now that we have the local in max space let
    // get the offset in max space and make them
    // our space
    //
    Point3 offset_trans = node->GetObjOffsetPos();
    Quat offset_rot = node->GetObjOffsetRot();
    
    Stuff::Point3D offset_translation;
    offset_translation=ConvertMaxToMW(offset_trans);
    
    Stuff::UnitQuaternion offset_rotation;
    offset_rotation=ConvertMaxToMW(offset_rot);
    
    Stuff::LinearMatrix4D offset_matrix = Stuff::LinearMatrix4D::Identity;
    offset_matrix.BuildRotation(offset_rotation);
    offset_matrix.BuildTranslation(offset_translation);
    //
    // now decompose the max matrix and make our linear matrix
    //
    AffineParts parts;
    decomp_affine(mat, &parts);
    
    Stuff::Point3D translation;
    translation=ConvertMaxToMW(parts.t);
    
    Stuff::UnitQuaternion rotation;
    rotation=ConvertMaxToMW(parts.q);
    
    Stuff::LinearMatrix4D local_matrix = Stuff::LinearMatrix4D::Identity;
    local_matrix.BuildRotation(rotation);
    local_matrix.BuildTranslation(translation);
    
    matrix->Multiply(offset_matrix,local_matrix);
    return *matrix != Stuff::LinearMatrix4D::Identity;
}

/*******************************************************************************
/* function name: TraverseTree
/* description: Traverse the armature hierarchy
/*******************************************************************************/
int ArmatureExporter::TraverseTree(INode *node, Stuff::NotationFile *armature_file)
{
    for (int c = 0; c < node->NumberOfChildren(); c++) {
        TraverseTree(node->GetChildNode(c),armature_file);
        
        INode *child = node->GetChildNode(c);
        CString name =child->GetName();
        name.Replace(' ', '_');
		name.MakeLower();

        CString prefix = name.Left(name.Find("_",0));
        
        if (!IsNodeCamera(child))
        {
            if (!(IsNodeGeometry(child)) && (!prefix.CompareNoCase("joint") || !prefix.CompareNoCase("site")))
            {
				Page *page = armature_file->SetPage(name);
				Check_Object(page);
				
                if (child->GetParentNode()->IsRootNode())
                {
                    page->AppendEntry("AttachedToRoot","true");
                }
                //
                // now get the matrix
                //
                Stuff::LinearMatrix4D mat;
                GetLocalToParent(child,&mat);
                
                
                if(mat!=Stuff::LinearMatrix4D::Identity)
                {
                    Stuff::UnitQuaternion quat_angle;
                    quat_angle = mat;
                    
                    Stuff::YawPitchRoll	  ypr_angle;
                    ypr_angle=quat_angle;
                    if(Stuff::Close_Enough(ypr_angle.yaw ,0.0f,0.0001f))
                        ypr_angle.yaw = 0.0;
                    if(Stuff::Close_Enough(ypr_angle.pitch ,0.0f,0.0001f))
                        ypr_angle.pitch = 0.0;
                    if(Stuff::Close_Enough(ypr_angle.roll ,0.0f,0.0001f))
                        ypr_angle.roll = 0.0;
                    
                    page->AppendEntry("Rotation" ,ypr_angle);
                    //
                    // write out the transform vector
                    //
                    Stuff::Point3D transform = Stuff::Point3D(0.0,0.0,0.0);
                    transform = mat;
                    
                    if(Stuff::Close_Enough(transform.x ,0.0f,0.0001f))
                        transform.x = 0.0;
                    if(Stuff::Close_Enough(transform.y ,0.0f,0.0001f))
                        transform.y = 0.0;
                    if(Stuff::Close_Enough(transform.z ,0.0f,0.0001f))
                        transform.z = 0.0;
                    
                    page->AppendEntry("Translation",transform);
                }
				
				if(name.Right(5).CompareNoCase("ankle")==NULL)
				{
					CString cname=name.Left(name.GetLength()-5)+"below"+name.Right(5);
					page->AppendEntry("Child", (LPCSTR)cname);
					name=cname;
					page = NULL;
				}
				
				//
				// now write out the list of children
				//
				CString data_filename  = "basic.data";
				int number_children = child->NumberOfChildren();

				for (int y=0; y < number_children; y++)
				{
					INode*  sib = child->GetChildNode(y);
					if (sib)
					{
						CString sib_name = sib->GetName();
						sib_name.Replace(' ', '_');
						if (IsNodeGeometry(sib))
						{
							data_filename  = sib_name;
							data_filename += ".data";
							break;
						}
						else if (y == child->NumberOfChildren()-1)
						{
							data_filename  = "basic.data";
						}
					}
				}
				
				for (int z=0; z < number_children; z++)
				{
					INode*  sib = child->GetChildNode(z);
					if (sib)
					{
						CString sibname= sib->GetName();
						CString sibprefix=sibname.Left(sibname.Find("_",0));
						//armature_file->SetEntry(name,"Model",data_filename);
						if (!IsNodeGeometry(sib) && (!sibprefix.CompareNoCase("joint") || !sibprefix.CompareNoCase("site")))
						{
							CString sib_name = sib->GetName();
							sib_name.Replace(' ', '_');
							if (!page)
								page = armature_file->SetPage(name);
							page->AppendEntry("Child", (const char *)sib_name);
						}
					}
				}
			}
		}
	}
    return 0;
}

