//----------------------------------------------------------------------------------
bool DumpModel(IGameMesh *gM, m_model *pModel, IGameNode *pGameNode)
{
    IGameSkin *skin = NULL;

    if (gM->InitializeData()) // prepare game data
    {
        GMatrix ObjectTM = pGameNode->GetObjectTM(ExporterMAX::GetExporter()->GetStaticFrame());

        Matrix3 world_to_obj = Inverse(ObjectTM.ExtractMatrix3());

        AffineParts	PRS;
        decomp_affine(ObjectTM.ExtractMatrix3(), &PRS);

        //Matrix xform;
        //
        //xform.set_rot(Quaternion(-PRS.q.x, -PRS.q.z, PRS.q.y, PRS.q.w));
        //xform.set_translation(Vector(-PRS.t.x, PRS.t.z, PRS.t.y));

        const int numMod = gM->GetNumModifiers();

        if (numMod > 0)
        {
            for (int i = 0; i < numMod; i++)     // check for skin modifier
            {
                IGameModifier * pM = gM->GetIGameModifier(i);
                if (pM->IsSkin()) {
                    skin = (IGameSkin*)pM; // skin modifier
                }
            }
        }

        mesh_opt       *m_opt;
        TriMapType      tri_map;
        MatFaceMapType  matface_map; // int <-> material

        unsigned int max_face_idx = 0;
        unsigned int FaceNum = Helper_GetNumberOfFaces(gM, FaceNum, max_face_idx);

        for (size_t i = 0; i < FaceNum; ++i)
        {
            Helper_ProcessFace(gM, i, PRS, world_to_obj, matface_map, tri_map, max_face_idx);
        }

        Helper_ComputeNormals(gM, matface_map);

        for (size_t IndexAdd = 0; IndexAdd < tri_map.size(); ++IndexAdd)
        {
            pModel->meshes.push_back(new m_mesh());
        }

        int count = 0;

        TriMapIt it = tri_map.begin();

        while (it != tri_map.end())
        {
            m_mesh &msh = *pModel->meshes[count];

            msh.num_faces = (*it).second->size() / 3;

            msh.material_id = (*it).first;

            msh.faces_idx = new unsigned int[msh.num_faces * 3];

            for (size_t i = 0; i < msh.num_faces * 3; i+=3)
            {
                int Idx0 = (*it).second->front();
                (*it).second->pop_front();

                int Idx1 = (*it).second->front();
                (*it).second->pop_front();

                int Idx2 = (*it).second->front();
                (*it).second->pop_front();

                msh.faces_idx[i+0] = Idx2;
                msh.faces_idx[i+1] = Idx1;
                msh.faces_idx[i+2] = Idx0;
            }

            MatFaceMapIt it_mapfacemap = matface_map.find((*it).first);
            assert(it_mapfacemap != matface_map.end());
            m_opt = (*it_mapfacemap).second;

            msh.skin = skin ? true : false;
            msh.num_vertices = m_opt->face_map.size();
            msh.vertices = new Vector[msh.num_vertices];
            msh.normals = new Vector[msh.num_vertices];
            msh.colors = new Vector4f[msh.num_vertices];

            msh.weights = skin ? new Vector4f[msh.num_vertices] : NULL;
            msh.bone_idxs = skin ? new unsigned int[msh.num_vertices * 4] : NULL;

            unsigned int texdim = 0;
            bool * faceidx_cache = new bool[msh.num_vertices];
            memset(faceidx_cache, 0, msh.num_vertices * sizeof(bool));
            bool alloc_texture = false;

            for (size_t i = 0; i < msh.num_faces * 3; ++i)
            {
                unsigned int face_idx = msh.faces_idx[i];
                FaceMapIt it_face_map = m_opt->face_map.find(face_idx);
                assert(it_face_map != m_opt->face_map.end());

                vert_opt face = (*it_face_map).second;

                msh.faces_idx[i] = face.face_idx;

                if (faceidx_cache[face.face_idx] == false)
                {
                    faceidx_cache[face.face_idx] = true;

                    msh.vertices[face.face_idx] = Vector(face.v.x, face.v.y, face.v.z);

                    msh.colors[face.face_idx].x = face.c.x;
                    msh.colors[face.face_idx].y = face.c.y;
                    msh.colors[face.face_idx].z = face.c.z;

                    Vector V(face.n.x, face.n.y, face.n.z);
                    V.normalize();
                    msh.normals[face.face_idx] = V;

                    if (msh.skin)
                    {
                        msh.weights[face.face_idx].x = face.weights.x;
                        msh.weights[face.face_idx].y = face.weights.y;
                        msh.weights[face.face_idx].z = face.weights.z;

                        msh.bone_idxs[face.face_idx * 4 + 0] = face.bones[0]; // already remapped idxs
                        msh.bone_idxs[face.face_idx * 4 + 1] = face.bones[1];
                        msh.bone_idxs[face.face_idx * 4 + 2] = face.bones[2];
                        msh.bone_idxs[face.face_idx * 4 + 3] = face.bones[3];
                    }

                    if (face.num_tmaps && alloc_texture == false)
                    {
                        alloc_texture = true;
                        texdim = 2;
                        msh.num_texcoord_sets = face.num_tmaps;
                        msh.texcoord_sets = new m_texcoord_set[msh.num_texcoord_sets];
                        for (size_t j = 0; j < face.num_tmaps; ++j)
                        {
                            msh.texcoord_sets[j].dim = texdim;
                            msh.texcoord_sets[j].texcoords = new float[msh.num_vertices * texdim];
                        }
                    }

                    for (size_t j = 0; j < face.num_tmaps; ++j)
                    {
                        msh.texcoord_sets[j].texcoords[face.face_idx * texdim] = face.tmaps[j].x;
                        msh.texcoord_sets[j].texcoords[face.face_idx * texdim + 1] = face.tmaps[j].y;
                    }

                    Vector tmp = face.v; //transform_coord(face.v, xform);
                    //mult_pos(tmp, xform, face.v);

                    // aabb min...
                    if (tmp.x < pModel->aabb_min.x)
                        pModel->aabb_min.x = tmp.x;
                    if (tmp.y < pModel->aabb_min.y)
                        pModel->aabb_min.y = tmp.y;
                    if (tmp.z < pModel->aabb_min.z)
                        pModel->aabb_min.z = tmp.z;
                    // aabb max...
                    if (tmp.x > pModel->aabb_max.x)
                        pModel->aabb_max.x = tmp.x;
                    if (tmp.y > pModel->aabb_max.y)
                        pModel->aabb_max.y = tmp.y;
                    if (tmp.z > pModel->aabb_max.z)
                        pModel->aabb_max.z = tmp.z;

                    // mesh bounding box
                    // aabb min...
                    if (tmp.x < msh.aabb_min.x)
                        msh.aabb_min.x = tmp.x;
                    if (tmp.y < msh.aabb_min.y)
                        msh.aabb_min.y = tmp.y;
                    if (tmp.z < msh.aabb_min.z)
                        msh.aabb_min.z = tmp.z;
                    // aabb max...
                    if (tmp.x > msh.aabb_max.x)
                        msh.aabb_max.x = tmp.x;
                    if (tmp.y > msh.aabb_max.y)
                        msh.aabb_max.y = tmp.y;
                    if (tmp.z > msh.aabb_max.z)
                        msh.aabb_max.z = tmp.z;
                }
            }
            delete [] faceidx_cache;
            ++count;
            ++it;

            Helper_ComputeUV(msh);
            Helper_ComputeTBN(msh);
        }
        return true;
    }
    return false; // "BadObject";
}
	// Load blend shape poses
	bool BlendShape::loadPoses(ParamList &params, std::vector<vertex> &vertices,long numVertices,long offset,long targetIndex)
	{
		if (params.useSharedGeom)
		{
			assert(targetIndex == 0);
			m_target = T_MESH;
		}
		else
		{
			assert(offset == 0);
			poseGroup new_pg;
			m_target = T_SUBMESH;
			new_pg.targetIndex = targetIndex;
			m_poseGroups.insert(std::pair<int,poseGroup>(targetIndex,new_pg));
		}
		poseGroup& pg = m_poseGroups.find(targetIndex)->second;

		if(m_pGameNode && m_pMorphR3)
		{
            // Disable all skin Modifiers.
            std::vector<Modifier*> disabledSkinModifiers;
            IGameObject* pGameObject = m_pGameNode->GetIGameObject();
            if( pGameObject )
            {
                int numModifiers = pGameObject->GetNumModifiers();
                for( int i = 0; i < numModifiers; ++i )
                {
                    IGameModifier* pGameModifier = pGameObject->GetIGameModifier(i);
                    if( pGameModifier )
                    {
                        if( pGameModifier->IsSkin() )
                        {
                            Modifier* pModifier = pGameModifier->GetMaxModifier();
                            if( pModifier )
                            {
                                if( pModifier->IsEnabled() )
                                {
                                    disabledSkinModifiers.push_back(pModifier);
                                    pModifier->DisableMod();
                                }
                            }
                        }
                    }
                }
            }

			// Get the original mesh from the IGameNode.  Not using IGame here
			// since MorphR3 doesn't allow for it.  Also we don't know if our vertices
			// are in object or world space, so we'll just calculate diffs directly from 
			// the Max meshes and modify the coordinate system manually.  
			// Obtained method of getting mesh from 3D Studio Max SDK Training session by
			// David Lanier.
 			bool DeleteObjectWhenDone;
			const ObjectState& objectState = m_pGameNode->GetMaxNode()->EvalWorldState(GetCOREInterface()->GetTime());
			Object *origMeshObj = objectState.obj;
			if (!origMeshObj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)))
			{
				FxOgreMaxExporterLog( "Could not access original mesh for morph target comparison.");
				return false;
			}

			// Calculate the DiffTM matrix.  This is the difference between the INode's world transform
			// which is used to calculate the morph verticies, and the IGameNode's world transform, which is used
			// to calculate the Ogre mesh's verticies.
			Matrix3 DiffTM = m_pGameNode->GetObjectTM(GetCOREInterface()->GetTime()).ExtractMatrix3();

			// The below code is not well tested as FaceFX needs content in the native coordinates.
			// I've seen the direction of the morph movement flipped on some content when in Y-up mode 
			// which sets the coordinate system to IGAME_OGL.
			// I can't get this to work on all the morph examples I have however.
			IGameConversionManager* pConversionManager = GetConversionManager();
			if(IGameConversionManager::IGAME_OGL == pConversionManager->GetCoordSystem())
			{			
				Matrix3 conv = Matrix3(Point3(1,0,0), Point3(0,0,1), Point3(0,-1,0), Point3(0,0,0));
				DiffTM = DiffTM * conv;
			}

			TriObject *origMeshTriObj = (TriObject *) origMeshObj->ConvertToType(GetCOREInterface()->GetTime(), Class_ID(TRIOBJ_CLASS_ID, 0));
			if (origMeshObj != origMeshTriObj) DeleteObjectWhenDone = true;
			Mesh& origMesh = origMeshTriObj->GetMesh();
			const int NumVerts = origMesh.getNumVerts();
 

			for( int i = 0; i < m_pMorphR3->chanBank.size() && i < MR3_NUM_CHANNELS; ++i )
			{
				if( m_pMorphR3->chanBank[i].mActive )
				{
					morphChannel* pMorphChannel = &m_pMorphR3->chanBank[i];	
					if( pMorphChannel )
					{
						pMorphChannel->rebuildChannel();

						std::string posename = string_tools::string_cast<ogre_string_type>(pMorphChannel->mName.data());
						int numMorphVertices = pMorphChannel->mNumPoints;
						
						if( numMorphVertices != origMesh.getNumVerts() )
						{
							MessageBox(GetCOREInterface()->GetMAXHWnd(), _T("Morph targets have failed to export becuase the morph vertex count did not match the base mesh.  Collapse the modifier stack prior to export, as smoothing is not supported with morph target export."), _T("Morph Target Export Failed."), MB_OK);
							return false;
						}
						else
						{
							FxOgreMaxExporterLog( "Exporting Morph target: %s with %d vertices.\n", posename.c_str(), numMorphVertices);
							FxOgreMaxExporterLog( "Mesh has %d vertices.\n", numVertices);
							FxOgreMaxExporterLog( "%d total vertices.\n", vertices.size());
							assert(offset+numVertices <= vertices.size());
							// create a new pose
							pose p;
							p.poseTarget = m_target;
							p.index = targetIndex;
							p.blendShapeIndex = i;
							p.name = posename;
							p.pMChannel = pMorphChannel;

							size_t numPoints = pMorphChannel->mPoints.size();
							std::vector<Point3> vmPoints;
							vmPoints.reserve(numPoints);
							for( size_t k = 0; k < numPoints; ++k )
							{
								vmPoints.push_back(pMorphChannel->mPoints[k]);
							}

							Box3 morphBoundingBox;
							// calculate vertex offsets
							for (int k=0; k<numVertices; k++)
							{
								vertexOffset vo;
								assert ((offset+k)<vertices.size());

								vertex v = vertices[offset+k];
								assert(v.index < numMorphVertices);
								assert(v.index < origMesh.getNumVerts());

								Point3 meshVert = origMesh.getVert(v.index);
								Point3 morphVert = vmPoints[v.index];

								Point3 diff = morphVert - meshVert;

								// Transform our morph vertex movements by whatever
								// scaling/rotation is being done by IGame..
								Point3 ogreSpacediff = DiffTM.VectorTransform(diff);


								// Add this point to the bounding box
								morphBoundingBox += morphVert;

								vo.x = ogreSpacediff.x * params.lum;
								vo.y = ogreSpacediff.y * params.lum;
								vo.z = ogreSpacediff.z * params.lum;	

								vo.index = offset+k;
								if (fabs(vo.x) < PRECISION)
									vo.x = 0;
								if (fabs(vo.y) < PRECISION)
									vo.y = 0;
								if (fabs(vo.z) < PRECISION)
									vo.z = 0;
								if ((vo.x!=0) || (vo.y!=0) || (vo.z!=0))
									p.offsets.push_back(vo);
							}
							// add pose to pose list
							if (p.offsets.size() > 0)
							{
								pg.poses.push_back(p);
							}
							if (params.bsBB)
							{
								// update bounding boxes of loaded submeshes
								for (int j=0; j<params.loadedSubmeshes.size(); j++)
								{
									Point3 min = morphBoundingBox.Min() * params.lum;
									Point3 max = morphBoundingBox.Max() * params.lum;
									// Update coordinate system here.
									Point3 newMin, newMax;
									newMin.x = min.x;
									newMin.y = min.z;
									newMin.z = min.y;
									Box3 newBox(newMin, newMax);
									if (params.exportWorldCoords)
										newBox = newBox * m_pGameNode->GetWorldTM(GetCOREInterface()->GetTime()).ExtractMatrix3();
									params.loadedSubmeshes[j]->m_boundingBox += newBox;
								}
							}
						}

					}
				}
			}
            // Re-enable skin modifiers.
            for( int i = 0; i < disabledSkinModifiers.size(); ++i )
            {
                disabledSkinModifiers[i]->EnableMod();
            }
			// According to David Lanier, this should be deleted, but I get crashes when exporting blendShapes
			// without shared geometry when I use the object for the second time.  Perhaps it
			// can only be used/deleted once.  Even without shared geometry, I'll get a strange crash
			// a few seconds after successful export with this here.
//			if (DeleteObjectWhenDone)
//				origMeshTriObj->DeleteMe();
		}
		return true;
	}
//----------------------------------------------------------------------------------
bool Helper_ProcessFace(IGameMesh * gM,
                        unsigned int Index,
                        const AffineParts & PRS,
                        const Matrix3 & world_to_obj,
                        MatFaceMapType & matface_map,
                        TriMapType     & tri_map,
                        unsigned int & MaxFaceIdx)
{
    FaceEx *pFace		= gM->GetFace(Index);
    IGameMaterial *pIGMat = gM->GetMaterialFromFace(Index);
    bool HasTexVerts	= gM->GetNumberOfTexVerts() ? true : false;
    bool HasCVerts		= gM->GetNumberOfColorVerts() ? true : false;
    unsigned int smg_id = pFace->smGrp; // smooth group
    unsigned int mat_id = ExporterMAX::GetExporter()->FindMaterialIdx(pIGMat);
    IGameSkin *skin = NULL;

    const int numMod = gM->GetNumModifiers();

    if (numMod > 0)
    {
        for (int i = 0; i < numMod; i++)     // check for skin modifier
        {
            IGameModifier *pM = gM->GetIGameModifier(i);
            if (pM->IsSkin()) {
                skin = (IGameSkin*)pM; // skin modifier
            }
        }
    }

    mesh_opt * m_opt = NULL;

    // lets sort by material and find the corresponding mesh_opt
    MatFaceMapIt it_matfacemap = matface_map.find(mat_id);

    if (it_matfacemap == matface_map.end()) // no corresponding mesh, allocate new face holder
    {
        m_opt = new mesh_opt();
        matface_map.insert(MatFaceMapPair(mat_id, m_opt));
    }
    else
    {
        m_opt = (*it_matfacemap).second;
    }

    for (int j = 0; j < 3; ++j) // build the face
    {
        vert_opt face;
        unsigned int idx;
        unsigned int ori_face_idx = pFace->vert[j]; // get index into the vertex array
        unsigned int face_idx = ori_face_idx;

        bool create_face = false;

        // build the face as expected
        face.smg_id = smg_id; // smooth group

        if (HasTexVerts)
        {
            idx = pFace->texCoord[j]; // get index into the standard mapping channel

            if (idx != BAD_IDX)
            {
                face.t.x = gM->GetTexVertex(idx).x;
                face.t.y = gM->GetTexVertex(idx).y;
            }
        }

        if (HasCVerts)
        {
            idx = pFace->color[j];
            face.c.x = gM->GetColorVertex(idx).x;  // get vertex color
            face.c.y = gM->GetColorVertex(idx).y;
            face.c.z = gM->GetColorVertex(idx).z;
            face.c.w = 1.f;

        } else {
            face.c = Vector4f(1.f, 1.f, 1.f, 1.f);
        }

        Tab<int> mapNums = gM->GetActiveMapChannelNum();
        face.num_tmaps = mapNums.Count();

        if (face.num_tmaps)
        {
            face.tmaps = new Vector[face.num_tmaps];
            for (size_t k = 0; k < face.num_tmaps; ++k)
            {
                unsigned long mapfaceidx[3];
                gM->GetMapFaceIndex(mapNums[k], Index, &mapfaceidx[0]);
                idx = mapfaceidx[j];
                face.tmaps[k].x = gM->GetMapVertex(mapNums[k], idx).x;
                face.tmaps[k].y = gM->GetMapVertex(mapNums[k], idx).y;
                face.tmaps[k].z = gM->GetMapVertex(mapNums[k], idx).z;
            }
        }

        // try to find in origin array
        FaceMapIt it_face_map = m_opt->face_map.find(face_idx);  // get face map iter

        if (it_face_map == m_opt->face_map.end()) // if not find such create anyway
        {
            create_face = true;
        }
        else
        {
            if (is_matching((*it_face_map).second, face) == false) // check find vertex not matching
            {
                bool found = false;

                // process vertex multi map
                std::pair<FaceMMapIt,FaceMMapIt> pair_mmap = m_opt->face_mmap.equal_range(ori_face_idx);

                FaceMMapIt it_face_mmap = pair_mmap.first;

                while (it_face_mmap != pair_mmap.second && found == false)
                {
                    idxvert_opt & idxface = (*it_face_mmap).second;

                    if (is_matching(idxface.face, face))
                    {
                        face_idx = idxface.new_idx;
                        found = true;
                    }
                    ++it_face_mmap;
                }

                if (found == false)
                {
                    create_face = true;
                    ++MaxFaceIdx;			// increment max index and
                    face_idx = MaxFaceIdx;	// set index is out of bounds of origin 3DMax's index range
                }
            }
        }

        if (create_face)
        {
            if (skin)
            {
                std::vector<w_offset>   w_offsets;
                for (int k = 0; k < skin->GetNumberOfBones(ori_face_idx); ++k)
                {
                    /*	if (skin->GetWeight(ori_face_idx, k) > m_eps)
                    	{
                    		w_offset w;
                    		w.weight = skin->GetWeight(ori_face_idx, k);

                    		_BoneObject * Bone = ExporterMAX::GetExporter()->Skeleton.AddMaxBone(skin->GetIGameBone(ori_face_idx, k), skin->GetIGameBone(ori_face_idx, k)->GetNodeID());
                    		assert(Bone != NULL);

                    		w.bone_id = Bone->GetID();
                    		w_offsets.push_back(w);
                    	}*/
                }

//				std::sort(w_offsets.begin(), w_offsets.end(), heavier);

                int ILeft = 0;
                for (size_t l = 0; l < w_offsets.size() && l < 4; ++l, ++ILeft)
                {
                    w_offset & w = w_offsets[l];
                    face.weights[l] = w.weight;
                    face.bones[l] = w.bone_id;
                }

                for (; ILeft < 4; ++ILeft)
                {
                    face.weights[ILeft] = 0.f;
                    face.bones[ILeft] = BAD_IDX;
                }

                // check for valid weights...
                float w_sum = 0.f;

                for (int l = 0; l < 4; ++l) {
                    w_sum += face.weights[l];
                }

                if ((w_sum < m_one - m_eps) || (w_sum > m_one + m_eps))
                {
                    for (int l = 0; l < 4; ++l) // renormalizing...
                        face.weights[l] /= w_sum;
                }
            }

            Point3 v_world = gM->GetVertex(ori_face_idx);
            Point3 v_obj = v_world; // * world_to_obj;

            face.v.x = v_obj.x; // * PRS.k.x * PRS.f;
            face.v.y = v_obj.z; // * PRS.k.z * PRS.f;
            face.v.z = v_obj.y; // * PRS.k.y * PRS.f;

            // add the vertex and store its new idx
            face.face_idx = m_opt->count;

            m_opt->face_map.insert(FaceMapPair(face_idx, face));

            if (ori_face_idx != face_idx)  // store the newly created and duplicated independently
            {
                idxvert_opt idxface(face_idx, face);// store new face
                m_opt->face_mmap.insert(FaceMMapPair(ori_face_idx, idxface)); // but store key as a original
            }
            m_opt->count++;
        }

        // add the face indices...
        TriMapIt it = tri_map.find(mat_id);
        if (it != tri_map.end())
        {
            (*it).second->push_back(face_idx);
        }
        else
        {
            IdxType * idx_type = new IdxType;
            idx_type->push_back(face_idx);
            tri_map.insert(TriMapPair(mat_id, idx_type));
        }
    }
    return true;
}