/*
**	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/>.
*/

/* $Header: /Commando/Code/Tools/max2w3d/hiersave.cpp 56    10/30/00 6:58p Greg_h $ */
/*********************************************************************************************** 
 ***                            Confidential - Westwood Studios                              *** 
 *********************************************************************************************** 
 *                                                                                             * 
 *                 Project Name : Commando / G 3D Engine                                       * 
 *                                                                                             * 
 *                     $Archive:: /Commando/Code/Tools/max2w3d/hiersave.cpp                   $* 
 *                                                                                             * 
 *                      $Author:: Greg_h                                                      $* 
 *                                                                                             * 
 *                     $Modtime:: 10/30/00 6:14p                                              $* 
 *                                                                                             * 
 *                    $Revision:: 56                                                          $* 
 *                                                                                             * 
 *---------------------------------------------------------------------------------------------* 
 * Functions:                                                                                  * 
 *   HierarchySaveClass::HierarchySaveClass -- constructor                                     * 
 *   HierarchySaveClass::HierarchySaveClass -- constructor                                     * 
 *   HierarchySaveClass::HierarchySaveClass -- constructor                                     * 
 *   HierarchySaveClass::~HierarchySaveClass -- destructor                                     * 
 *   HierarchySaveClass::Free -- releases all allocated memory                                 * 
 *   HierarchySaveClass::Get_Node_Transform -- returns the transformation matrix of specified n* 
 *   HierarchySaveClass::get_relative_transform -- retruns tm between this node and its parent * 
 *   HierarchySaveClass::Get_Name -- returns the name of this hierarchy                        * 
 *   HierarchySaveClass::Get_Node -- Get the Max INode                                         *
 *   HierarchySaveClass::Get_Node_Name -- returns name of this hierarchy node                  * 
 *   HierarchySaveClass::Find_Named_Node -- returns index of a named node                      * 
 *   HierarchySaveClass::Get_Export_Coordinate_System - find the bone and coordinate system    *
 *   HierarchySaveClass::Save -- write the hierarchy into a W3D file                           * 
 *   HierarchySaveClass::Load -- read the hierarchy from a W3D file                            * 
 *   HierarchySaveClass::add_tree -- adds a node and all of its children                       * 
 *   HierarchySaveClass::add_node -- adds a single node to the tree                            * 
 *   HierarchySaveClass::Get_Fixup_Transform -- gets the "fixup" transform for a node          * 
 *   HierarchySaveClass::fixup_matrix -- conditions a matrix                                   * 
 *   HierarchySaveClass::save_header -- writes the header into a W3D file                      * 
 *   HierarchySaveClass::save_pivots -- writes the pivots into a W3D file                      * 
 *   HierarchySaveClass::save_fixups -- writes the fixup transforms into a W3D file            * 
 *   HierarchySaveClass::load_header -- reads the header from a W3D file                       * 
 *   HierarchySaveClass::load_pivots -- reads the pivots from a W3D file                       * 
 *   HierarchySaveClass::load_fixups -- reads the fixup transforms from a W3D file             * 
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "hiersave.h"
#include "w3d_file.h"
#include "nodefilt.h"
#include "euler.h"
#include "util.h"
#include "w3dappdata.h"
#include "errclass.h"
#include "exportlog.h"


bool HierarchySaveClass::TerrainModeEnabled = false;


/*********************************************************************************************** 
 * HierarchySaveClass::HierarchySaveClass -- constructor                                       * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 * root - root INode to construct the HTree from                                               * 
 * time - current time in Max, transforms at this time will be used                            * 
 * treemeter - progress meter                                                                  * 
 * hname - name for the hierarchy tree                                                         * 
 * fixup_type - can be used to force all transforms to be translation only                     * 
 * fixuptree - htree loaded from a previous export                                             * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
HierarchySaveClass::HierarchySaveClass
(
	INode * root,
	TimeValue time,
	Progress_Meter_Class & treemeter,
	char * hname,
	int fixuptype,
	HierarchySaveClass * fixuptree
) : 
	Node(DEFAULT_NODE_ARRAY_SIZE),
	CurNode(0),
	FixupType(fixuptype),
	FixupTree(fixuptree)
{
	CurNode = 0;
	CurTime = time;

	/*
	** This code-path is activated when the user has created a custom origin.  In this case, we 
	** need to compute the transform which will make all bones relative to this origin.
	*/
	OriginOffsetTransform = Inverse(root->GetNodeTM(CurTime));
	
	/*
	** Build our tree from the given tree of nodes
	*/
	int rootidx = add_node(NULL,-1);
 	assert(rootidx == 0);
	add_tree(root,rootidx);

	HierarchyHeader.Version = W3D_CURRENT_HTREE_VERSION;
	Set_W3D_Name(HierarchyHeader.Name,hname);
	HierarchyHeader.NumPivots = CurNode;
	HierarchyHeader.Center.X = 0.0f;
	HierarchyHeader.Center.Y = 0.0f;
	HierarchyHeader.Center.Z = 0.0f;
}

/*********************************************************************************************** 
 * HierarchySaveClass::HierarchySaveClass -- constructor                                       * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * rootlist - list of root nodes to add to the htree                                           * 
 * time - current time in Max, transforms at this time will be used                            * 
 * treemeter - progress meter                                                                  * 
 * hname - name for the hierarchy tree                                                         * 
 * fixup_type - can be used to force all transforms to be translation only                     * 
 * fixuptree - htree loaded from a previous export                                             * 
 * origin_offset - origin offset transform                                                     * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
HierarchySaveClass::HierarchySaveClass
(
	INodeListClass * rootlist,
	TimeValue time,
	Progress_Meter_Class & treemeter,
	char * hname,
	int fixuptype,
	HierarchySaveClass * fixuptree,
	const Matrix3 & origin_offset
) : 
	Node(DEFAULT_NODE_ARRAY_SIZE),
	CurNode(0),
	FixupType(fixuptype),
	FixupTree(fixuptree),
	OriginOffsetTransform(origin_offset)
{
	CurNode = 0;
	CurTime = time;

	/*
	** Build the tree with all leaves of all of the nodes given
	*/
	int rootidx = add_node(NULL,-1);
 	assert(rootidx == 0);

	for (unsigned int i = 0; i < rootlist->Num_Nodes(); i++) {
		add_tree((*rootlist)[i],rootidx);
	}

	HierarchyHeader.Version = W3D_CURRENT_HTREE_VERSION;
	Set_W3D_Name(HierarchyHeader.Name,hname);
	HierarchyHeader.NumPivots = CurNode;
	HierarchyHeader.Center.X = 0.0f;
	HierarchyHeader.Center.Y = 0.0f;
	HierarchyHeader.Center.Z = 0.0f;

}

/*********************************************************************************************** 
 * HierarchySaveClass::HierarchySaveClass -- constructor                                       * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
HierarchySaveClass::HierarchySaveClass():
	Node(NULL),
	CurNode(0),
	CurTime(0)
{
}

/*********************************************************************************************** 
 * HierarchySaveClass::~HierarchySaveClass -- destructor                                       * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
HierarchySaveClass::~HierarchySaveClass(void)
{
	Free();
}

/*********************************************************************************************** 
 * HierarchySaveClass::Free -- releases all allocated memory                                   * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
void HierarchySaveClass::Free(void)
{
	Node.Clear();
}


/*********************************************************************************************** 
 * HierarchySaveClass::Get_Node_Transform -- returns the transformation matrix of specified no * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
Matrix3 HierarchySaveClass::Get_Node_Transform(int nodeidx) const
{
	Matrix3 tm(1);

	int idx = nodeidx;

	while (idx != -1) {
		tm = tm * get_relative_transform(idx);
		idx = Node[idx].Pivot.ParentIdx;
	}

	return tm;
}


/*********************************************************************************************** 
 * HierarchySaveClass::get_relative_transform -- retruns tm between this node and its parent   * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
Matrix3 HierarchySaveClass::get_relative_transform(int nodeidx) const
{
	assert(nodeidx >= 0);
	assert(nodeidx < CurNode);

	Point3	trans;
	Quat		rot;
	Matrix3	tm(true);
	Matrix3	rtm(true);

	trans.x = Node[nodeidx].Pivot.Translation.X;
	trans.y = Node[nodeidx].Pivot.Translation.Y;
	trans.z = Node[nodeidx].Pivot.Translation.Z;

	//	WARNING! I had to fudge the orientation
	// quaternion (Max's representation seems to
	// rotate in the opposite sense as mine...)
	rot[0] = -Node[nodeidx].Pivot.Rotation.Q[0];
	rot[1] = -Node[nodeidx].Pivot.Rotation.Q[1];
	rot[2] = -Node[nodeidx].Pivot.Rotation.Q[2];
	rot[3] = Node[nodeidx].Pivot.Rotation.Q[3];

	tm.Translate(trans);
	rot.MakeMatrix(rtm);
	tm = rtm * tm;

	return tm;
}


/*********************************************************************************************** 
 * HierarchySaveClass::Get_Name -- returns the name of this hierarchy                          * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
const char * HierarchySaveClass::Get_Name(void) const
{
	return HierarchyHeader.Name;
}


/***********************************************************************************************
 * HierarchySaveClass::Get_Node -- Get the Max INode                                           *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   1/15/98    GTH : Created.                                                                 *
 *=============================================================================================*/
INode * HierarchySaveClass::Get_Node(int node) const
{
	assert(node >= 0);
	assert(node < CurNode);

	return Node[node].MaxNode;	
}

/*********************************************************************************************** 
 * HierarchySaveClass::Get_Node_Name -- returns name of this hierarchy node                    * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
const char * HierarchySaveClass::Get_Node_Name(int node) const
{
	assert(node >= 0);
	assert(node < CurNode);

	return Node[node].Pivot.Name;
}


/*********************************************************************************************** 
 * HierarchySaveClass::Find_Named_Node -- returns index of a named node                        * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
int HierarchySaveClass::Find_Named_Node(const char * name) const
{
	int match = -1;
	for (int index=0; index<CurNode; index++) {
		if (strcmp(Node[index].Pivot.Name,name) == 0) {
			match = index;
		}
	}

	return match;
}

/***********************************************************************************************
 * HierarchySaveClass::Get_Export_Coordinate_System - find the bone and coordinate system      *
 * for the given object                                                                        *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/17/2000 gth : Created.                                                                 *
 *=============================================================================================*/
void HierarchySaveClass::Get_Export_Coordinate_System
(
	INode * node,
	int * set_bone_index,
	INode ** set_bone_node,
	Matrix3 * set_transform
)
{
	/*
	** find the first parent of this node which
	** is in the base pose.  Note, we're finding the parent bone
	** in our hierarchy with the same name as a bone in the "main"
	** hierarchy.  When we're exporting LOD models, there are multiple
	** hierarchies...
	*/
	bool	done = false;
	int	boneidx = -1;
	INode * pbone = node;

	while (!done) {

		char name[W3D_NAME_LEN];
		Set_W3D_Name(name,pbone->GetName());

		boneidx = Find_Named_Node(name);

		if (boneidx != -1) {

			/*
			** We found the parent bone!
			*/
			done = true;

		} else if (Is_Origin(pbone)) {

			/*
			** Don't go up past our origin, use this as our bone.
			*/
			boneidx = 0;
			done = true;

		} else {

			/*
			** Nope, try the next parent
			*/
			pbone = pbone->GetParentNode();
			assert(pbone != NULL);

#if 0
			if (pbone == NULL) {

				/*
				** mesh isn't connected to a bone, use the root
				*/
				boneidx = 0;
				pbone = node;
				done = true;
			}
#endif
		}
	}
	
	if (set_bone_index != NULL) {
		*set_bone_index = boneidx;
	}
	if (set_bone_node != NULL) {
		*set_bone_node = pbone;
	}
	if (set_transform != NULL) {
		*set_transform = Get_Fixup_Transform(boneidx) * pbone->GetNodeTM(CurTime);
	}
}

/*********************************************************************************************** 
 * HierarchySaveClass::Save -- write the hierarchy into a W3D file                             * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
bool HierarchySaveClass::Save(ChunkSaveClass & csave)
{
	ExportLog::printf("\nSaving Hierarchy Tree %s.\n",HierarchyHeader.Name);
	ExportLog::printf("Node Count: %d\n",CurNode);
	ExportLog::printf("Nodes: \n");
	for (int inode = 0; inode < CurNode; inode++) {
		ExportLog::printf("  %s\n",Node[inode].Pivot.Name);
	}

	if (!csave.Begin_Chunk(W3D_CHUNK_HIERARCHY)) {
		return false;
	}

	if (!save_header(csave)) {
		return false;
	}

	if (!save_pivots(csave)) {
		return false;
	}

	if (!save_fixups(csave)) {
		return false;
	}

	if (!csave.End_Chunk()) {
		return false;
	}
	
	return true;
}


/*********************************************************************************************** 
 * HierarchySaveClass::Load -- read the hierarchy from a W3D file                              * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
bool HierarchySaveClass::Load(ChunkLoadClass & cload)
{
	Free();
	bool error = false;

	while (cload.Open_Chunk()) {
		switch (cload.Cur_Chunk_ID()) {
			case W3D_CHUNK_HIERARCHY_HEADER:
				if (!load_header(cload)) error = true;
				break;
			case W3D_CHUNK_PIVOTS:
				if (!load_pivots(cload)) error = true;				
				break;
			case W3D_CHUNK_PIVOT_FIXUPS:
				if (!load_fixups(cload)) error = true;
				break;
			default:
				break;
		}

		if (!cload.Close_Chunk() || error) {
			return false;
		}
	}

	CurNode = HierarchyHeader.NumPivots;

	return true;
}


/*********************************************************************************************** 
 * HierarchySaveClass::add_tree -- adds a node and all of its children                         * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
void HierarchySaveClass::add_tree(INode * node,int pidx)
{
	int nextparent;

	if (node->IsHidden ()) {

		// if the node is hidden, do not add it but add its children to the current parent.
		nextparent = pidx;			

	} else if (TerrainModeEnabled && (Is_Normal_Mesh(node) || Is_Null_Object(node))) {

		// terrain optimization, normal meshes are not allowed to have transforms
		nextparent = pidx;

	} else if (!Is_Bone(node)) {
	
		// This node isn't a bone, don't add it
		nextparent = pidx;
	
	} else {

		// Add new pivot!	it will be parent of all below it.	
		nextparent = add_node(node,pidx);
	
	}

	// Add all of this nodes children
	for (int i=0; i < node->NumberOfChildren(); i++) {
		add_tree(node->GetChildNode(i),nextparent);
	}
}


/*********************************************************************************************** 
 * HierarchySaveClass::add_node -- adds a single node to the tree                              * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
int HierarchySaveClass::add_node(INode * node,int pidx)
{
	/*
	** 'grow' the node array if necessary
	*/
	if (CurNode >= Node.Length ()) {
		Node.Resize (Node.Length () + NODE_ARRAY_GROWTH_SIZE);
	}

	/*
	** setup the pivot
	*/
	Node[CurNode].MaxNode = node;
	Node[CurNode].Pivot.ParentIdx = pidx;

	if (node) {
		Set_W3D_Name(Node[CurNode].Pivot.Name,node->GetName());
	} else {
		Set_W3D_Name(Node[CurNode].Pivot.Name,"RootTransform");
	}		

	/*
	** Now, check if there is a bone with this W3D name already
	** if there is, scold the user and bail out
	*/
	if (Find_Named_Node(Node[CurNode].Pivot.Name) != -1) {
		char buf[128];
		sprintf(buf,"Bones with duplicate names found!\nDuplicated Name: %s\n",Node[CurNode].Pivot.Name);
		throw ErrorClass(buf);
	}

	/*
	** Compute the transformation for this node
	*/
	Matrix3		maxnodeTM(1);
	Matrix3		ournodeTM(1);
	Matrix3		fixupTM(1);
	Point3		trans(0,0,0);
	Quat			rot(1);
	Point3		scale(1,1,1);

	if (node) {
		maxnodeTM = node->GetNodeTM(CurTime) * OriginOffsetTransform;
	} else {
		maxnodeTM = Matrix3(1);
	}

	/*
	** If this tree is being "fixed up" the first thing we do
	** is to transform Max's nodeTM by the fixup transform.
	** This is done when a base pose was created using our own
	** types of transforms and we want to apply the same 
	** changes to this tree.  
	**
	** Note that if FixupType is not "NONE", FixupTree must be NULL,
	*/
	assert(!((FixupTree != NULL) && (FixupType != MATRIX_FIXUP_NONE)));

	if (FixupTree != NULL) {
		int fi = FixupTree->Find_Named_Node(Node[CurNode].Pivot.Name);
		if (fi == -1) {
			char buf[128];
			sprintf(buf,"Incompatible Base Pose!\nMissing Bone: %s\n",Node[CurNode].Pivot.Name);
			throw ErrorClass(buf);
		}

		Matrix3 fixup = FixupTree->Get_Fixup_Transform(fi);

		maxnodeTM = fixup * maxnodeTM;
	}


	ournodeTM = fixup_matrix(maxnodeTM);
	fixupTM = ournodeTM * Inverse(maxnodeTM);

	/*
	** Now, make ournodeTM relative to its parent transform.  We
	** will always store relative transformations.  (Also, note
	** that it is relative to our version of the parent transform
	** which is not necessarily the same as the MAX version...)
	*/
	if (pidx != -1) {
		Matrix3 parentTM = Get_Node_Transform(pidx);
		Matrix3 pinv = Inverse(parentTM);
		ournodeTM = ournodeTM * pinv;
	}


	/*
	** Break the matrix down into a rotation and translation.
	*/
	DecomposeMatrix(ournodeTM,trans,rot,scale);
	
	/*
	** Save the "fixup" matrix
	*/
	for (int j=0;j<4;j++) {
		Point3 row = fixupTM.GetRow(j);
		Node[CurNode].Fixup.TM[j][0] = row.x;
		Node[CurNode].Fixup.TM[j][1] = row.y;
		Node[CurNode].Fixup.TM[j][2] = row.z;
	}

	/*
	** Set the translation and rotation for this pivot.
	*/
	Node[CurNode].Pivot.Translation.X	= trans.x;
	Node[CurNode].Pivot.Translation.Y	= trans.y;
	Node[CurNode].Pivot.Translation.Z	= trans.z;

	Node[CurNode].Pivot.Rotation.Q[0] = -rot[0];
	Node[CurNode].Pivot.Rotation.Q[1] = -rot[1];
	Node[CurNode].Pivot.Rotation.Q[2] = -rot[2];
	Node[CurNode].Pivot.Rotation.Q[3] = rot[3];

	/*
	** Compute the Euler angles and set them.
	*/
	Matrix3 rotmat;
	rot.MakeMatrix(rotmat);
	EulerAnglesClass eangs(rotmat,EulerOrderXYZr);

	Node[CurNode].Pivot.EulerAngles.X = eangs.Get_Angle(0);
	Node[CurNode].Pivot.EulerAngles.Y = eangs.Get_Angle(1);
	Node[CurNode].Pivot.EulerAngles.Z = eangs.Get_Angle(2);

	return CurNode++;
}

/*********************************************************************************************** 
 * HierarchySaveClass::Get_Fixup_Transform -- gets the "fixup" transform for a node            * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
Matrix3 HierarchySaveClass::Get_Fixup_Transform(int node) const
{
	assert(node >= 0);
	assert(node < CurNode);

	Matrix3 m;
	
	for (int j=0;j<4;j++) {
		m.SetRow(j,Point3(Node[node].Fixup.TM[j][0],Node[node].Fixup.TM[j][1],Node[node].Fixup.TM[j][2]));
	}
		
	return m;
}

/*********************************************************************************************** 
 * HierarchySaveClass::fixup_matrix -- conditions a matrix                                     * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
Matrix3 HierarchySaveClass::fixup_matrix(const Matrix3 & csrc) const
{
	Matrix3	src = csrc;	// the GetTrans function is not const correct...
	Matrix3	newtm(1);
	Point3	trans;
	Quat		rot;
	Point3	scale;

	switch (FixupType) {
		case MATRIX_FIXUP_NONE:
			newtm = src;
			break;

		case MATRIX_FIXUP_TRANS:
			newtm.SetTrans(src.GetTrans());
			newtm = Cleanup_Orthogonal_Matrix(newtm);
			break;

		case MATRIX_FIXUP_TRANS_ROT:
			DecomposeMatrix(src,trans,rot,scale);
			rot.MakeMatrix(newtm);
			newtm.SetTrans(trans);
			newtm = Cleanup_Orthogonal_Matrix(newtm);
			break;
	};

	return newtm;
}


/*********************************************************************************************** 
 * HierarchySaveClass::save_header -- writes the header into a W3D file                        * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
bool HierarchySaveClass::save_header(ChunkSaveClass & csave)
{
	if (!csave.Begin_Chunk(W3D_CHUNK_HIERARCHY_HEADER)) {
		return false;
	}

	if (csave.Write(&HierarchyHeader,sizeof(HierarchyHeader)) != sizeof(HierarchyHeader)) {
		return false;
	}

	if (!csave.End_Chunk()) {
		return false;
	}

	return true;
}


/*********************************************************************************************** 
 * HierarchySaveClass::save_pivots -- writes the pivots into a W3D file                        * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
bool HierarchySaveClass::save_pivots(ChunkSaveClass & csave)
{
	if (!csave.Begin_Chunk(W3D_CHUNK_PIVOTS)) {
		return false;
	}

	for (uint32 i=0; i<HierarchyHeader.NumPivots; i++) {
		if (csave.Write(&Node[i].Pivot,sizeof(W3dPivotStruct)) != sizeof(W3dPivotStruct)) {
			return false;
		}
	}

	if (!csave.End_Chunk()) {
		return false;
	}

	return true;
}

/*********************************************************************************************** 
 * HierarchySaveClass::save_fixups -- writes the fixup transforms into a W3D file              * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
bool HierarchySaveClass::save_fixups(ChunkSaveClass & csave)
{
	if (!csave.Begin_Chunk(W3D_CHUNK_PIVOT_FIXUPS)) {
		return false;
	}

	for (uint32 i=0; i<HierarchyHeader.NumPivots; i++) {
		if (csave.Write(&Node[i].Fixup,sizeof(W3dPivotFixupStruct)) != sizeof(W3dPivotFixupStruct)) {
			return false;
		}
	}

	if (!csave.End_Chunk()) {
		return false;
	}

	return true;
}


/*********************************************************************************************** 
 * HierarchySaveClass::load_header -- reads the header from a W3D file                         * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
bool HierarchySaveClass::load_header(ChunkLoadClass & cload)
{
	/*
	** Load the header
	*/
	if (cload.Read(&HierarchyHeader,sizeof(HierarchyHeader)) != sizeof(HierarchyHeader)) {
		return false;
	}

	/*
	** Reset the current node count
	*/
	CurNode = 0;
	Node.Resize(HierarchyHeader.NumPivots);

	/*
	** Initialize everything to a default state (particularly the
	** fixup matrices to identity...)
	*/
	for (unsigned i=0; i < HierarchyHeader.NumPivots; i++) {
		memset(&(Node[i]),0,sizeof(HierarchyNodeStruct));

		Matrix3 ident(1);
		for (int j=0; j<3; j++) {
			Point3 row = ident.GetRow(j);
			Node[i].Fixup.TM[j][0] = row.x;
			Node[i].Fixup.TM[j][1] = row.y;
			Node[i].Fixup.TM[j][2] = row.z;
		}
	}
	
	return true;
}


/*********************************************************************************************** 
 * HierarchySaveClass::load_pivots -- reads the pivots from a W3D file                         * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
bool HierarchySaveClass::load_pivots(ChunkLoadClass & cload)
{
	for (uint32 i=0; i<HierarchyHeader.NumPivots; i++) {
		Node[i].MaxNode = NULL;
		if (cload.Read(&Node[i].Pivot,sizeof(W3dPivotStruct)) != sizeof(W3dPivotStruct)) {
			return false;
		}		
	}
	return true;
}

/*********************************************************************************************** 
 * HierarchySaveClass::load_fixups -- reads the fixup transforms from a W3D file               * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   10/26/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
bool HierarchySaveClass::load_fixups(ChunkLoadClass & cload)
{
	for (uint32 i=0; i<HierarchyHeader.NumPivots; i++) {
		if (cload.Read(&Node[i].Fixup,sizeof(W3dPivotFixupStruct)) != sizeof(W3dPivotFixupStruct)) {
			return false;
		}		
	}
	return true;
}


