///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Mesh export method.
 *	This method is called once for each exported mesh.
 *	\param		mesh		[in] a structure filled with current mesh information.
 *	\return		true if success.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ASCIIFormat::ExportMesh(const MeshDescriptor& mesh)
{
	// Export common information
	mGeomObjects.StoreASCII("\n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n").StoreASCII("GeomObject:   ");
	ExportBasicInfo(&mesh, &mGeomObjects);

	// Export mesh parameters
	if(mesh.mIsCollapsed)		mGeomObjects.StoreASCII("(Collapsed object)\n");
	if(mesh.mIsSkeleton)		mGeomObjects.StoreASCII("(Skeleton object)\n");
	if(mesh.mIsInstance)		mGeomObjects.StoreASCII("(Instance object)\n");
	if(mesh.mIsTarget)			mGeomObjects.StoreASCII("(Target object)\n");
	if(!mesh.mIsConvertible)	mGeomObjects.StoreASCII("(Non convertible object)\n");
	if(mesh.mIsSkin)			mGeomObjects.StoreASCII("(Skin)\n");
	if(mesh.mCastShadows)		mGeomObjects.StoreASCII("(Can cast shadows)\n");

	// Export character ID for BIPED parts and skins
	if(mesh.mIsSkeleton || mesh.mIsSkin)
	{
		mGeomObjects.StoreASCII("CharID: ").StoreASCII(mesh.mCharID).StoreASCII("\n");
	}

	// Export the bone's CSID for BIPED parts
	if(mesh.mIsSkeleton)
	{
		mGeomObjects.StoreASCII("CSID:   ").StoreASCII(mesh.mCSID).StoreASCII("\n");
	}

	if(mesh.mIsInstance)
	{
		mGeomObjects.StoreASCII("Instance from: ").StoreASCII((const char*)mesh.mMasterMesh->mName).StoreASCII("\n");
	}
	else
	{
		// Export MAX native mesh
		mGeomObjects.StoreASCII("\nMAX native mesh data:\n");
		const MAXNativeMesh* Mesh = &mesh.mOriginalMesh;

		mGeomObjects.StoreASCII(Mesh->mNbFaces).StoreASCII(" faces\n");
		mGeomObjects.StoreASCII(Mesh->mNbVerts).StoreASCII(" vertices\n");
		mGeomObjects.StoreASCII(Mesh->mNbTVerts).StoreASCII(" mapping coordinates\n");
		mGeomObjects.StoreASCII(Mesh->mNbCVerts).StoreASCII(" vertex colors\n");

		if(Mesh->mParity)	mGeomObjects.StoreASCII("Parity is true.\n");
		else				mGeomObjects.StoreASCII("Parity is false.\n");

		// Export vertices
		if(Mesh->mVerts)
		{
			mGeomObjects.StoreASCII("\nVertices:\n");
			MAXPoint* v = Mesh->mVerts;
			for(udword i=0;i<Mesh->mNbVerts;i++)
			{
				mGeomObjects.StoreASCII(v[i].x).StoreASCII(" ").StoreASCII(v[i].y).StoreASCII(" ").StoreASCII(v[i].z).StoreASCII("\n");
			}
		}

		// If the mesh is a skin, export skin-related data
		if(mesh.mIsSkin)
		{
			// Export links to the driving skeleton
			if(!Mesh->mBonesNb)
			{
				// A simple skin with one bone/vertex.
				MAXPoint* v = Mesh->mOffsetVectors;
				if(v)
				{
					mGeomObjects.StoreASCII("\nOffset vectors:\n");
					for(udword i=0;i<Mesh->mNbVerts;i++)
					{
						mGeomObjects.StoreASCII(v[i].x).StoreASCII(" ").StoreASCII(v[i].y).StoreASCII(" ").StoreASCII(v[i].z).StoreASCII("\n");
					}
				}

				if(Mesh->mBonesID)
				{
					mGeomObjects.StoreASCII("\nBones ID:\n");
					for(udword i=0;i<Mesh->mNbVerts;i++)
					{
						mGeomObjects.StoreASCII(Mesh->mBonesID[i]).StoreASCII(" ");
						if((i&63)==63)	mGeomObjects.StoreASCII("\n");
					}
					mGeomObjects.StoreASCII("\n");
				}
			}
			else
			{
				// A skin with multiple bones/vertex.
				mGeomObjects.StoreASCII("\nBones Nb:\n");
				udword Sum = 0;
				for(udword i=0;i<Mesh->mNbVerts;i++)
				{
					mGeomObjects.StoreASCII(Mesh->mBonesNb[i]).StoreASCII(" ");
					if((i&63)==63)	mGeomObjects.StoreASCII("\n");
					Sum+=Mesh->mBonesNb[i];
				}
				mGeomObjects.StoreASCII("\n");

				if(Mesh->mBonesID)
				{
					mGeomObjects.StoreASCII("\nBones ID:\n");
					for(udword i=0;i<Sum;i++)
					{
						mGeomObjects.StoreASCII(Mesh->mBonesID[i]).StoreASCII(" ");
						if((i&63)==63)	mGeomObjects.StoreASCII("\n");
					}
				}
				mGeomObjects.StoreASCII("\n");

				if(Mesh->mWeights)
				{
					mGeomObjects.StoreASCII("\nWeights:\n");
					for(udword i=0;i<Sum;i++)
					{
						mGeomObjects.StoreASCII(Mesh->mWeights[i]).StoreASCII(" ");
						if((i&63)==63)	mGeomObjects.StoreASCII("\n");
					}
				}
				mGeomObjects.StoreASCII("\n");

				if(Mesh->mOffsetVectors)
				{
					MAXPoint* v = Mesh->mOffsetVectors;
					mGeomObjects.StoreASCII("\nOffset vectors:\n");
					for(udword i=0;i<Sum;i++)
					{
						mGeomObjects.StoreASCII(v[i].x).StoreASCII(" ").StoreASCII(v[i].y).StoreASCII(" ").StoreASCII(v[i].z).StoreASCII("\n");
					}
				}
				mGeomObjects.StoreASCII("\n");
			}

			// Export compatible skeletal information
			Skeleton* Skel = Mesh->mSkeleton;
			mGeomObjects.StoreASCII("\nSkeletal information: ").StoreASCII(Skel->mNbBones).StoreASCII(" bones:\n");
			mGeomObjects.StoreASCII("(CSID, parent CSID)\n");
			udword* ID = Skel->mID;
			for(udword i=0;i<Skel->mNbBones;i++)
			{
				udword CSID = *ID++;
				udword pCSID = *ID++;
				mGeomObjects.StoreASCII(CSID).StoreASCII(" ").StoreASCII(pCSID).StoreASCII("\n");
			}
		}

		// Export UVW mappings
		if(Mesh->mTVerts)
		{
			mGeomObjects.StoreASCII("\nMapping coordinates:\n");
			MAXPoint* v = Mesh->mTVerts;
			for(udword i=0;i<Mesh->mNbTVerts;i++)
			{
				mGeomObjects.StoreASCII(v[i].x).StoreASCII(" ").StoreASCII(v[i].y).StoreASCII(" ").StoreASCII(v[i].z).StoreASCII("\n");
			}
		}

		// Export vertex-colors
		if(Mesh->mCVerts)
		{
			mGeomObjects.StoreASCII("\nVertex colors:\n");
			MAXPoint* v = Mesh->mCVerts;
			for(udword i=0;i<Mesh->mNbCVerts;i++)
			{
				mGeomObjects.StoreASCII(v[i].x).StoreASCII(" ").StoreASCII(v[i].y).StoreASCII(" ").StoreASCII(v[i].z).StoreASCII("\n");
			}
		}

		// Export faces
		if(Mesh->mFaces)
		{
			mGeomObjects.StoreASCII("\nFaces:\n(vref0 vref1 vref2  tref0 tref1 tref2  cref0 cref1 cref2  MatID Smg EdgeVisCode)\n");
			MAXFace* f = Mesh->mFaces;
			for(udword i=0;i<Mesh->mNbFaces;i++)
			{
				mGeomObjects
					.StoreASCII(f[i].VRef[0]).StoreASCII(" ").StoreASCII(f[i].VRef[1]).StoreASCII(" ").StoreASCII(f[i].VRef[2]).StoreASCII("  ")
					.StoreASCII(f[i].TRef[0]).StoreASCII(" ").StoreASCII(f[i].TRef[1]).StoreASCII(" ").StoreASCII(f[i].TRef[2]).StoreASCII("  ")
					.StoreASCII(f[i].CRef[0]).StoreASCII(" ").StoreASCII(f[i].CRef[1]).StoreASCII(" ").StoreASCII(f[i].CRef[2]).StoreASCII("  ")
					.StoreASCII(f[i].MatID).StoreASCII(" ").StoreASCII(f[i].Smg).StoreASCII(" ").StoreASCII(f[i].EdgeVis).StoreASCII("\n");
			}
		}

		// Export the convex hull
		if(Mesh->mFlags&MESH_CONVEXHULL)
		{
			mGeomObjects.StoreASCII("\nConvex hull:");
			mGeomObjects.StoreASCII("\n").StoreASCII(Mesh->mConvexHull->mNbVerts).StoreASCII(" vertices");
			mGeomObjects.StoreASCII("\n").StoreASCII(Mesh->mConvexHull->mNbFaces).StoreASCII(" faces\n");

			if(Mesh->mConvexHull->mVerts)
			{
				mGeomObjects.StoreASCII("\nConvex hull vertices:\n");
				MAXPoint* v = Mesh->mConvexHull->mVerts;
				for(udword i=0;i<Mesh->mConvexHull->mNbVerts;i++)
				{
					mGeomObjects.StoreASCII(v[i].x).StoreASCII(" ").StoreASCII(v[i].y).StoreASCII(" ").StoreASCII(v[i].z).StoreASCII("\n");
				}
			}

			if(Mesh->mConvexHull->mFaces)
			{
				mGeomObjects.StoreASCII("\nConvex hull faces:\n(vref0 vref1 vref2)\n");
				udword* f = Mesh->mConvexHull->mFaces;
				for(udword i=0;i<Mesh->mConvexHull->mNbFaces;i++)
				{
					mGeomObjects.StoreASCII(f[i*3+0]).StoreASCII(" ").StoreASCII(f[i*3+1]).StoreASCII(" ").StoreASCII(f[i*3+2]).StoreASCII("\n");
				}
			}
		}

		// Export the bounding sphere
		if(Mesh->mFlags&MESH_BOUNDINGSPHERE)
		{
			mGeomObjects
				.StoreASCII("\nBounding sphere:")
				.StoreASCII("\nCenter: ").StoreASCII(Mesh->mBSCenter.x).StoreASCII(" ").StoreASCII(Mesh->mBSCenter.y).StoreASCII(" ").StoreASCII(Mesh->mBSCenter.z)
				.StoreASCII("\nRadius: ").StoreASCII(Mesh->mBSRadius).StoreASCII("\n");
		}

		// Export the inertia tensor
		if(Mesh->mFlags&MESH_INERTIATENSOR)
		{
			ITensor* T = Mesh->mTensor;
			mGeomObjects
				.StoreASCII("\nVolume integrals:")

				.StoreASCII("\nCOM:  ").StoreASCII(T->COM.x).StoreASCII(" ").StoreASCII(T->COM.y).StoreASCII(" ").StoreASCII(T->COM.z)

				.StoreASCII("\nMass: ").StoreASCII(T->Mass).StoreASCII("\n")

				.StoreASCII("\nInertia tensor:\n")
				.StoreASCII(T->InertiaTensor[0][0]).StoreASCII(" ").StoreASCII(T->InertiaTensor[0][1]).StoreASCII(" ").StoreASCII(T->InertiaTensor[0][2]).StoreASCII("\n")
				.StoreASCII(T->InertiaTensor[1][0]).StoreASCII(" ").StoreASCII(T->InertiaTensor[1][1]).StoreASCII(" ").StoreASCII(T->InertiaTensor[1][2]).StoreASCII("\n")
				.StoreASCII(T->InertiaTensor[2][0]).StoreASCII(" ").StoreASCII(T->InertiaTensor[2][1]).StoreASCII(" ").StoreASCII(T->InertiaTensor[2][2]).StoreASCII("\n")

				.StoreASCII("\nCOM Inertia tensor:\n")
				.StoreASCII(T->COMInertiaTensor[0][0]).StoreASCII(" ").StoreASCII(T->COMInertiaTensor[0][1]).StoreASCII(" ").StoreASCII(T->COMInertiaTensor[0][2]).StoreASCII("\n")
				.StoreASCII(T->COMInertiaTensor[1][0]).StoreASCII(" ").StoreASCII(T->COMInertiaTensor[1][1]).StoreASCII(" ").StoreASCII(T->COMInertiaTensor[1][2]).StoreASCII("\n")
				.StoreASCII(T->COMInertiaTensor[2][0]).StoreASCII(" ").StoreASCII(T->COMInertiaTensor[2][1]).StoreASCII(" ").StoreASCII(T->COMInertiaTensor[2][2]).StoreASCII("\n");
		}

		// Export consolidated mesh
		if(Mesh->mFlags&MESH_CONSOLIDATION)
		{
			mGeomObjects.StoreASCII("\nCONSOLIDATION\n");

			// Topology
			{
				MBTopology& Topo = mesh.mCleanMesh->Topology;

				mGeomObjects
				.StoreASCII("\nTOPOLOGY\n")
				.StoreASCII("\nNb faces:      ").StoreASCII(Topo.NbFaces)
				.StoreASCII("\nNb submeshes:  ").StoreASCII(Topo.NbSubmeshes)
				.StoreASCII("\n");

				// Submeshes
				for(udword i=0;i<Topo.NbSubmeshes;i++)
				{
					MBSubmesh* CurSM = &Topo.SubmeshProperties[i];
					mGeomObjects
					.StoreASCII("\nSubmesh #").StoreASCII(i)
					.StoreASCII("\nMaterial ID:   ").StoreASCII(CurSM->MatID)
					.StoreASCII("\nSmGrps:        ").StoreASCII(CurSM->SmGrp)
					.StoreASCII("\nNbFaces:       ").StoreASCII(CurSM->NbFaces)
					.StoreASCII("\nNbVerts:       ").StoreASCII(CurSM->NbVerts)
					.StoreASCII("\nNbSubstrips:   ").StoreASCII(CurSM->NbSubstrips)
					.StoreASCII("\n");
				}

				// Connectivity
				uword* VRefs = Topo.VRefs;
				for(i=0;i<Topo.NbSubmeshes;i++)
				{
					uword NbFaces = Topo.FacesInSubmesh[i];

					mGeomObjects.StoreASCII("\nSubmesh #").StoreASCII(i).StoreASCII(" (").StoreASCII(NbFaces).StoreASCII(" faces)\n");

					// Save faces (Ref0, Ref1, Ref2)
					for(uword j=0;j<NbFaces;j++)
					{
						uword Ref0 = *VRefs++;
						uword Ref1 = *VRefs++;
						uword Ref2 = *VRefs++;
						mGeomObjects.StoreASCII(Ref0).StoreASCII(" ").StoreASCII(Ref1).StoreASCII(" ").StoreASCII(Ref2).StoreASCII("\n");
					}
				}

				// Face normals
				mGeomObjects.StoreASCII("\nFace normals:\n");
				float* Normals = Topo.Normals;
				if(Normals)
				{
					for(i=0;i<Topo.NbFaces;i++)
					{
						float nx = Normals[i*3+0];
						float ny = Normals[i*3+1];
						float nz = Normals[i*3+2];
						mGeomObjects.StoreASCII(nx).StoreASCII(" ").StoreASCII(ny).StoreASCII(" ").StoreASCII(nz).StoreASCII("\n");
					}
				}
				else mGeomObjects.StoreASCII("(not available)\n");
			}

			// Geometry
			{
				MBGeometry& Geo = mesh.mCleanMesh->Geometry;

				mGeomObjects
				.StoreASCII("\nGEOMETRY\n")
				.StoreASCII("\nNb GeomPts:    ").StoreASCII(Geo.NbGeomPts)
				.StoreASCII("\nNb Verts:      ").StoreASCII(Geo.NbVerts)
				.StoreASCII("\nNb TVerts:     ").StoreASCII(Geo.NbTVerts)
				.StoreASCII("\n");

				// Indexed geometry
				udword MaxRef=0;
				if(Geo.VertsRefs)
				{
					mGeomObjects.StoreASCII("\nIndexed geometry:\n");
					for(udword i=0;i<Geo.NbVerts;i++)
					{
						udword Ref = Geo.VertsRefs[i];
						if(Ref>MaxRef)	MaxRef=Ref;
						mGeomObjects.StoreASCII(Ref).StoreASCII(" ");
						if((i&63)==63)	mGeomObjects.StoreASCII("\n");
					}
					mGeomObjects.StoreASCII("\n");
				}

				// Vertices
				udword NbVerts = Geo.VertsRefs ? (MaxRef+1) : Geo.NbVerts;
				mGeomObjects.StoreASCII("\nVertices: (").StoreASCII(NbVerts).StoreASCII(")\n");
				for(udword i=0;i<NbVerts;i++)
				{
					float x = Geo.Verts[i*3+0];
					float y = Geo.Verts[i*3+1];
					float z = Geo.Verts[i*3+2];
					mGeomObjects.StoreASCII(x).StoreASCII(" ").StoreASCII(y).StoreASCII(" ").StoreASCII(z).StoreASCII(" ").StoreASCII("\n");
				}
				mGeomObjects.StoreASCII("\n");

				// Indexed UVWs
				MaxRef = 0;
				if(Geo.TVertsRefs)
				{
					mGeomObjects.StoreASCII("\nIndexed UVWs:\n");
					for(udword i=0;i<Geo.NbVerts;i++)
					{
						udword Ref = Geo.TVertsRefs[i];
						if(Ref>MaxRef)	MaxRef=Ref;
						mGeomObjects.StoreASCII(Ref).StoreASCII(" ");
						if((i&63)==63)	mGeomObjects.StoreASCII("\n");
					}
					mGeomObjects.StoreASCII("\n");
				}

				// Vertices
				if(mSettings.mExpUVW && Geo.TVerts)
				{
					udword NbTVerts = Geo.TVertsRefs ? (MaxRef+1) : Geo.NbVerts;
					mGeomObjects.StoreASCII("\nUVW mappings: (").StoreASCII(NbTVerts).StoreASCII(")\n");
					float* p = Geo.TVerts;
					bool UseW = !mSettings.mDiscardW;
					for(udword i=0;i<NbTVerts;i++)
					{
						float u = *p++;	mGeomObjects.StoreASCII(u).StoreASCII(" ");
						float v = *p++;	mGeomObjects.StoreASCII(v).StoreASCII(" ");
						if(UseW)
						{
							float w = *p++;	mGeomObjects.StoreASCII(w).StoreASCII(" ");
						}
						mGeomObjects.StoreASCII("\n");
					}
				}

				// Normals
				if(Geo.Normals)
				{
					udword NbNormals = Geo.NbVerts;
					mGeomObjects.StoreASCII("\nVertex normals: (").StoreASCII(NbNormals).StoreASCII(")\n");
					for(udword i=0;i<NbNormals;i++)
					{
						float x = Geo.Normals[i*3+0];
						float y = Geo.Normals[i*3+1];
						float z = Geo.Normals[i*3+2];
						mGeomObjects.StoreASCII(x).StoreASCII(" ").StoreASCII(y).StoreASCII(" ").StoreASCII(z).StoreASCII(" ").StoreASCII("\n");
					}
				}
				else mGeomObjects.StoreASCII("\nVertex normals: (not available)\n");

				// Vertex colors
				if(mSettings.mExpVTXColor && Geo.CVerts)
				{
					udword NbVtxColors = Geo.NbVerts;
					mGeomObjects.StoreASCII("\nVertex colors: (").StoreASCII(NbVtxColors).StoreASCII(")\n");
					for(udword i=0;i<NbVtxColors;i++)
					{
						float r = Geo.Normals[i*3+0];
						float g = Geo.Normals[i*3+1];
						float b = Geo.Normals[i*3+2];
						mGeomObjects.StoreASCII(r).StoreASCII(" ").StoreASCII(g).StoreASCII(" ").StoreASCII(b).StoreASCII(" ").StoreASCII("\n");
					}
				}

				// Normal info
				if(mSettings.mExportNormalInfo && Geo.NormalInfo)
				{
					udword NormalInfoSize = Geo.NormalInfoSize;
					mGeomObjects.StoreASCII("\nNormalInfo: (").StoreASCII(NormalInfoSize).StoreASCII(")\n");
					for(udword i=0;i<NormalInfoSize;i++)
					{
						udword d = Geo.NormalInfo[i];
						mGeomObjects.StoreASCII(d).StoreASCII(" ");
						if((i&63)==63)	mGeomObjects.StoreASCII("\n");
					}
				}
			}

			// Materials
			{
				MBMaterials& Mtl = mesh.mCleanMesh->Materials;

				mGeomObjects
				.StoreASCII("\nMATERIALS\n")
				.StoreASCII("\nNb materials:  ").StoreASCII(Mtl.NbMtls)
				.StoreASCII("\n");

				for(udword i=0;i<Mtl.NbMtls;i++)
				{
					MBMatInfo* CurMtl = &Mtl.MaterialInfo[i];

					mGeomObjects
					.StoreASCII("\nMaterial #").StoreASCII(i).StoreASCII(":")
					.StoreASCII("\nID:          ").StoreASCII(CurMtl->MatID)
					.StoreASCII("\nNbFaces:     ").StoreASCII(CurMtl->NbFaces)
					.StoreASCII("\nNbVerts:     ").StoreASCII(CurMtl->NbVerts)
					.StoreASCII("\nNbSubmeshes: ").StoreASCII(CurMtl->NbSubmeshes)
					.StoreASCII("\n");
				}
			}
		}
	}

	return true;
}
