Exemple #1
0
//-----------------------------------------------------------------------------
BOOL CExporter::Capture()
{
	VERIFY				(m_Style!=eExportUndef);
	Modifier*			pPhysique;
	IPhysiqueExport*	pExport;
	IPhyContextExport*	pContext;
	Object*				pObject;

	Matrix3				matMesh;
	Matrix3				matZero;

	if (!m_MeshNode){
		ERR("Select mesh and try again.");
		m_bHasError=TRUE; 
		return FALSE;
	}

	
	pObject				= m_MeshNode->GetObjectRef();
	if (!IsExportableMesh(m_MeshNode,pObject)){
		ERR("Can't receive node references.");
		m_bHasError=TRUE; 
		return FALSE;
	}

	// Get export interface
	pPhysique			= FindPhysiqueModifier(m_MeshNode);
	if (!pPhysique){
		ERR("Can't find Physique modifier.");
		m_bHasError=TRUE; 
		return FALSE;
	}
	pExport		= (IPhysiqueExport *)pPhysique->GetInterface(I_PHYINTERFACE);
	if (!pExport){
		ERR("Can't find Physique interface.");
		m_bHasError=TRUE; 
		return FALSE;
	}

	// Get mesh initial transform (used to mult by the bone matrices)
	int rval = CGINTM(m_MeshNode,pExport->GetInitNodeTM(m_MeshNode, matMesh));
	matZero.Zero();
	if (rval || matMesh.Equals(matZero, 0.0)){
		ERR("Old CS version. Can't export mesh");
		matMesh.IdentityMatrix();
	}

	// Add hierrarhy parts that has no effect on vertices, 
	// but required for hierrarhy stability

	if (eExportMotion==m_Style){
		if (m_AllBones.empty()){
			ERR("Invalid skin object. Bone not found.");
			return FALSE;
		}

		EConsole.ProgressStart((float)m_AllBones.size(),"..Capturing bones");
		for (DWORD i=0; i<m_AllBones.size(); i++){
			AddBone(m_AllBones[i], matMesh, pExport);
			EConsole.ProgressInc();
		}
		EConsole.ProgressEnd();
	}

	bool bRes = TRUE;
	if (eExportSkin==m_Style){
		// For a given Object's INode get a
		// ModContext Interface from the Physique Export Interface:
		pContext = (IPhyContextExport *)pExport->GetContextInterface(m_MeshNode);
		if (!pContext){
			ERR("Can't find Physique context interface.");
			return FALSE;
		}

		// convert to rigid with blending
		pContext->ConvertToRigid(TRUE);
		pContext->AllowBlending	(TRUE);

		// process vertices
		int	numVertices = pContext->GetNumberVertices();
		EConsole.ProgressStart(float(numVertices),"..Capturing vertices");
		for (int iVertex = 0; iVertex < numVertices; iVertex++ ){
			IPhyVertexExport *pVertexExport = (IPhyVertexExport *)pContext->GetVertexInterface(iVertex);
			R_ASSERT(pVertexExport);

			// What kind of vertices are these?
			int iVertexType = pVertexExport->GetVertexType();

			IPhyRigidVertex* pRigidVertex=(IPhyRigidVertex*)pContext->GetVertexInterface(iVertex);
			R_ASSERT					(pRigidVertex);
			switch (iVertexType){
			case RIGID_TYPE:{			
				INode* node				= pRigidVertex->GetNode(); 
				R_ASSERT				(node);
				LPCSTR nm				= node->GetName();
				// get bone and create vertex
				CVertexDef* pVertex		= AddVertex();
				int boneId				= AddBone(node,matMesh,pExport);
				if(BONE_NONE==boneId){
					ERR					("Invalid bone: ",node->GetName());
					bRes				= FALSE;
				}else pVertex->Append	(boneId,1.f);
							}break;
			case RIGID_BLENDED_TYPE:{
				IPhyBlendedRigidVertex*	pBlendedRigidVertex=(IPhyBlendedRigidVertex*)pRigidVertex;
				int cnt					= pBlendedRigidVertex->GetNumberNodes();
				CVertexDef* pVertex		= AddVertex();
				for (int i=0; i<cnt; i++){
					INode* node			= pBlendedRigidVertex->GetNode(i); 
					R_ASSERT			(node);
					LPCSTR nm			= node->GetName();
					// get bone and create vertex
					int boneId			= AddBone(node,matMesh,pExport);
					if(BONE_NONE==boneId){
						ERR				("Invalid bone: ",node->GetName());
						bRes			= FALSE;
					}else pVertex->Append(boneId,pBlendedRigidVertex->GetWeight(i));
				}
									}break;
			}

			// release vertex
			pContext->ReleaseVertexInterface( pRigidVertex );

			EConsole.ProgressInc();

			if (!bRes) break;
		}
		EConsole.ProgressEnd();

		if (!bRes) return FALSE;

		static int remap[3];
		if (U.m_SkinFlipFaces){
			remap[0] = 0;
			remap[1] = 1;
			remap[2] = 2;
		}else{
			remap[0] = 0;
			remap[1] = 2;
			remap[2] = 1;
		}

		// Process mesh
		// Get object from node. Abort if no object.
		Log("..Transforming mesh");
		BOOL		bDeleteTriObject;
		R_ASSERT	(pObject);
		TriObject *	pTriObject	= GetTriObjectFromObjRef(pObject, &bDeleteTriObject);
		if (!pTriObject){
			ERR("Can't create tri object.");
			return FALSE;
		}
		Mesh&		M = pTriObject->mesh;

		// Vertices
		{
			// check match with
			int iNumVert = M.getNumVerts();
			if (!(iNumVert==numVertices && iNumVert==m_Vertices.size()))
			{
				ERR("Non attached vertices found.");
				if (bDeleteTriObject)	delete(pTriObject);
				return FALSE;
			}

			// transform vertices
			for (int i=0; i<iNumVert; i++){
				Point3 P = M.getVert(i);
				Point3 T = matMesh.PointTransform(P);
				T *= m_fGlobalScale;
				m_Vertices[i]->SetPosition(T);
			}
		}

		Log("..Parsing materials");
		// Parse Materials
		m_MtlMain = m_MeshNode->GetMtl();
		R_ASSERT(m_MtlMain);

		DWORD cSubMaterials=m_MtlMain->NumSubMtls();
		if (cSubMaterials < 1) {
			// Count the material itself as a submaterial.
			cSubMaterials = 1;
		}

		// build normals
		M.buildRenderNormals();

		Log("..Converting vertices");
		// our Faces and Vertices
		{
			for (int i=0; i<M.getNumFaces(); i++){
				Face*	gF	= M.faces  + i;
				TVFace*	tF	= M.tvFace + i;

				int m_id = gF->getMatID();
				if (cSubMaterials == 1){
					m_id = 0;
				}else{
					// SDK recommends mod'ing the material ID by the valid # of materials, 
					// as sometimes a material number that's too high is returned.
					m_id %= cSubMaterials;
				}

				st_FACE* nF		= xr_new<st_FACE>();
				nF->m_id		= m_id;
				nF->sm_group	= gF->getSmGroup();
				for (int VVV=0; VVV<3; VVV++){
					int vert_id = gF->v[remap[VVV]];

					CVertexDef	&D	= *(m_Vertices[vert_id]);
					Point3		&UV	= M.tVerts[tF->t[remap[VVV]]];

					st_VERT		v;
					v.Set		(D);
					v.P.set		(D.P); 
					v.SetUV		(UV.x,1-UV.y);
//					v.sm_group	= U.m_SkinSuppressSmoothGroup?0:gF->getSmGroup(); // smooth group
					nF->v[VVV]	= AddVert(v);
				}
				m_ExpFaces.push_back(nF);
			}
		}
		if (bDeleteTriObject)	delete(pTriObject);
	}
	UpdateParenting();

	return bRes;
};
Exemple #2
0
CVertexCandidate *CMaxMesh::GetVertexCandidate(CSkeletonCandidate *pSkeletonCandidate, int faceId, int faceVertexId)
{
    // check for valid mesh and physique modifier
    if((m_pIMesh == 0))
    {
        theExporter.SetLastError("Invalid handle.", __FILE__, __LINE__);
        return 0;
    }

    // check if face id is valid
    if((faceId < 0) || (faceId >= m_pIMesh->getNumFaces()))
    {
        theExporter.SetLastError("Invalid face id found.", __FILE__, __LINE__);
        return 0;
    }

    // check if face vertex id is valid
    if((faceVertexId < 0) || (faceVertexId >= 3))
    {
        theExporter.SetLastError("Invalid face vertex id found.", __FILE__, __LINE__);
        return 0;
    }

    // allocate a new vertex candidate
    CVertexCandidate *pVertexCandidate;
    pVertexCandidate = new CVertexCandidate();
    if(pVertexCandidate == 0)
    {
        theExporter.SetLastError("Memory allocation failed.", __FILE__, __LINE__);
        return 0;
    }

    // create the new vertex candidate
    if(!pVertexCandidate->Create())
    {
        delete pVertexCandidate;
        return 0;
    }

    // get vertex id
    int vertexId;
    vertexId = m_pIMesh->faces[faceId].v[faceVertexId];

    // get the absolute vertex position
    Point3 vertex;
    vertex = m_pIMesh->getVert(vertexId) * m_tm;

    // set the vertex candidate position
    pVertexCandidate->SetPosition(vertex.x, vertex.y, vertex.z);
    pVertexCandidate->SetUniqueId(vertexId);

    // get the absolute vertex normal
    Point3 normal;
    normal = GetVertexNormal(faceId, vertexId);
    normal = normal * Inverse(Transpose(m_tm));
    normal = normal.Normalize();

    // set the vertex candidate normal
    pVertexCandidate->SetNormal(normal.x, normal.y, normal.z);
 
  if(m_pIMesh->numCVerts > 0)
  { 
        VertColor vc;
        vc = m_pIMesh->vertCol[m_pIMesh->vcFace[faceId].t[faceVertexId]];
    CalVector vcCal(vc.x, vc.y, vc.z);
    pVertexCandidate->SetVertColor(vcCal);
  }  
 
    // get the vertex weight array
    float *pVertexWeights;
    pVertexWeights = m_pIMesh->getVertexWeights();
  //if( pVertexWeights == NULL ) {
    //    delete pVertexCandidate;
    //    theExporter.SetLastError("Mesh has no vertex weights", __FILE__, __LINE__);
    //    return 0;
  //}

    // get the vertex weight (if possible)
    float weight;
    if(pVertexWeights != 0)
    {
        weight = pVertexWeights[vertexId];
    }
    else
    {
        weight = 0.0f;
    }

    // another 3ds max weird behaviour:
    // zero out epsilon weights
    if(weight < 0.0005f) weight = 0.0f;

    // set the vertex candidate weight
    pVertexCandidate->SetPhysicalProperty(weight);

    // get the material id of the face
    int materialId;
    materialId = GetFaceMaterialId(faceId);

    if((materialId < 0) || (materialId >= (int)m_vectorStdMat.size()))
    {
        delete pVertexCandidate;
        theExporter.SetLastError("Invalid material id found.", __FILE__, __LINE__);
        return 0;
    }

    // get the material of the face
    StdMat *pStdMat;
    pStdMat = m_vectorStdMat[materialId];

    // loop through all the mapping channels and extract texture coordinates
    int mapId;
    for(mapId = 0; mapId < pStdMat->NumSubTexmaps(); mapId++)
    {
        // get texture map
        Texmap *pTexMap;
        pTexMap = pStdMat->GetSubTexmap(mapId);

        // check if map is valid
        if((pTexMap != 0) && (pStdMat->MapEnabled(mapId)))
        {
            // get the mapping channel
            int channel;
            channel = pTexMap->GetMapChannel();

            bool bValidUV;
            bValidUV = false;

            // extract the texture coordinate
            UVVert uvVert;
            if(m_pIMesh->mapSupport(channel))
            {
                TVFace *pTVFace;
                pTVFace = m_pIMesh->mapFaces(channel);

                UVVert *pUVVert;
                pUVVert = m_pIMesh->mapVerts(channel);

                uvVert = pUVVert[pTVFace[faceId].t[faceVertexId]];
                bValidUV = true;
            }
            else if(m_pIMesh->numTVerts > 0)
            {
                uvVert = m_pIMesh->tVerts[m_pIMesh->tvFace[faceId].t[faceVertexId]];
                bValidUV = true;
            }

            // if we found valid texture coordinates, add them to the vertex candidate
            if(bValidUV)
            {
                // apply a possible uv generator
                StdUVGen *pStdUVGen;
                pStdUVGen = (StdUVGen *)pTexMap->GetTheUVGen();
                if(pStdUVGen != 0)
                {
                    Matrix3 tmUV;
                    pStdUVGen->GetUVTransform(tmUV);
                    uvVert = uvVert * tmUV;
                }

                // add texture coordinate to the vertex candidate, inverting the y coordinate
                pVertexCandidate->AddTextureCoordinate(uvVert.x, 1.0f - uvVert.y);
            }
        }
    }

    // check for physique modifier
    if(m_modifierType == MODIFIER_PHYSIQUE)
    {
        // create a physique export interface
        IPhysiqueExport *pPhysiqueExport;
        pPhysiqueExport = (IPhysiqueExport *)m_pModifier->GetInterface(I_PHYINTERFACE);
        if(pPhysiqueExport == 0)
        {
            delete pVertexCandidate;
            theExporter.SetLastError("Physique modifier interface not found.", __FILE__, __LINE__);
            return 0;
        }

        // create a context export interface
        IPhyContextExport *pContextExport;
        pContextExport = (IPhyContextExport *)pPhysiqueExport->GetContextInterface(m_pINode);
        if(pContextExport == 0)
        {
            m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);
            delete pVertexCandidate;
            theExporter.SetLastError("Context export interface not found.", __FILE__, __LINE__);
            return 0;
        }

        // set the flags in the context export interface
        pContextExport->ConvertToRigid(TRUE);
        pContextExport->AllowBlending(TRUE);

        // get the vertex export interface
        IPhyVertexExport *pVertexExport;
        pVertexExport = (IPhyVertexExport *)pContextExport->GetVertexInterface(vertexId);
        if(pVertexExport == 0)
        {
            pPhysiqueExport->ReleaseContextInterface(pContextExport);
            m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);
            delete pVertexCandidate;
            theExporter.SetLastError("Vertex export interface not found.", __FILE__, __LINE__);
            return 0;
        }

        // get the vertex type
        int vertexType;
        vertexType = pVertexExport->GetVertexType();

        // handle the specific vertex type
        if(vertexType == RIGID_TYPE)
        {
            // typecast to rigid vertex
            IPhyRigidVertex *pTypeVertex;
            pTypeVertex = (IPhyRigidVertex *)pVertexExport;

                // add the influence to the vertex candidate
            // get the influencing bone
            if(!AddBoneInfluence(pSkeletonCandidate, pVertexCandidate, pTypeVertex->GetNode(), 1.0f))
            {
                pPhysiqueExport->ReleaseContextInterface(pContextExport);
                m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);
                delete pVertexCandidate;
                theExporter.SetLastError("Invalid bone assignment.", __FILE__, __LINE__);
                return 0;
            }
        }
        else if(vertexType == RIGID_BLENDED_TYPE)
        {
            // typecast to blended vertex
            IPhyBlendedRigidVertex *pTypeVertex;
            pTypeVertex = (IPhyBlendedRigidVertex *)pVertexExport;

            // loop through all influencing bones
            int nodeId;
            for(nodeId = 0; nodeId < pTypeVertex->GetNumberNodes(); nodeId++)
            {
                // add the influence to the vertex candidate
                if(!AddBoneInfluence(pSkeletonCandidate, pVertexCandidate, pTypeVertex->GetNode(nodeId), pTypeVertex->GetWeight(nodeId)))
                {
                    pPhysiqueExport->ReleaseContextInterface(pContextExport);
                    m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);
                    delete pVertexCandidate;
                    theExporter.SetLastError("Invalid bone assignment.", __FILE__, __LINE__);
                    return 0;
                }
            }
        }

        // release all interfaces
        pPhysiqueExport->ReleaseContextInterface(pContextExport);
        m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);
    }
#if MAX_RELEASE >= 4000
    // check for skin modifier
    else if(m_modifierType == MODIFIER_SKIN)
    {
        // create a skin interface
        ISkin *pSkin;
        pSkin = (ISkin*)m_pModifier->GetInterface(I_SKIN);
        if(pSkin == 0)
        {
            delete pVertexCandidate;
            theExporter.SetLastError("Skin modifier interface not found.", __FILE__, __LINE__);
            return 0;
        }

        // create a skin context data interface
        ISkinContextData *pSkinContextData;
        pSkinContextData = (ISkinContextData *)pSkin->GetContextInterface(m_pINode);
        if(pSkinContextData == 0)
        {
            m_pModifier->ReleaseInterface(I_SKIN, pSkin);
            delete pVertexCandidate;
            theExporter.SetLastError("Skin context data interface not found.", __FILE__, __LINE__);
            return 0;
        }

        // loop through all influencing bones
        int nodeId;
        for(nodeId = 0; nodeId < pSkinContextData->GetNumAssignedBones(vertexId); nodeId++)
        {
            // get the bone id
            int boneId;
            boneId = pSkinContextData->GetAssignedBone(vertexId, nodeId);
            if(boneId < 0) continue;

            // add the influence to the vertex candidate
            if(!AddBoneInfluence(pSkeletonCandidate, pVertexCandidate, pSkin->GetBone(boneId), pSkinContextData->GetBoneWeight(vertexId, nodeId)))
            {
                m_pModifier->ReleaseInterface(I_SKIN, pSkin);
                delete pVertexCandidate;
                theExporter.SetLastError("Invalid bone assignment.", __FILE__, __LINE__);
                return 0;
            }
        }

        // release all interfaces
        m_pModifier->ReleaseInterface(I_SKIN, pSkin);
    }
#endif
        else if( m_modifierType == MODIFIER_MORPHER || m_modifierType == MODIFIER_NONE ) {
        }
        else 
    {
          theExporter.SetLastError("No physique/skin/morpher modifier found.", __FILE__, __LINE__);
          return 0;
    }

    return pVertexCandidate;
}
// --[  Method  ]---------------------------------------------------------------
//
//  - Class     : CStravaganzaMaxTools
//
//  - prototype : bool AddNodeBones(std::vector<INode*> &vecMaxBones, INode *pMaxNode)
//
//  - Purpose   : Adds all bones belonging to the node to the given list.
//
// -----------------------------------------------------------------------------
bool CStravaganzaMaxTools::AddNodeBones(std::vector<INode*> &vecMaxBones, INode *pMaxNode)
{
	int  nCount       = 0;
	int  nBoneCount   = 0;
	bool bOK          = true;

	Modifier          *pPhyModifier  = NULL; // Physique modifier
	IPhysiqueExport   *pPhyExport    = NULL; // Physique export interface
	IPhyContextExport *pPhyObjExport = NULL; // Physique object export interface

	// Get Physique modifier

	if(pPhyModifier = GetPhysiqueModifier(pMaxNode))
	{
		pPhyExport = (IPhysiqueExport *)pPhyModifier->GetInterface(I_PHYINTERFACE);

		if(pPhyExport == NULL)
		{
			return false;
		}
	}
	else
	{
		return false;
	}

	// Get physique object export interface

	pPhyObjExport = pPhyExport->GetContextInterface(pMaxNode);

	if(pPhyObjExport == NULL)
	{
		pPhyModifier->ReleaseInterface(I_PHYINTERFACE, pPhyExport);
		return false;
	}

	pPhyObjExport->ConvertToRigid(true);
	pPhyObjExport->AllowBlending(true);

	INode *pMaxBone;

	// Build bone list

	LOG.Write("\nObject %s Bone List:", pMaxNode->GetName());

	for(nCount = 0; nCount < pPhyObjExport->GetNumberVertices(); nCount++)
	{
		IPhyVertexExport       *pPhyVertExport;
		IPhyBlendedRigidVertex *pPhyBRVertexExport;
		IPhyRigidVertex        *pPhyRigidVertexExport;
		IPhyFloatingVertex	   *pPhyFloatingVertex;

		pPhyVertExport = pPhyObjExport->GetVertexInterface(nCount);

		if(pPhyVertExport)
		{
			if(pPhyVertExport->GetVertexType() & BLENDED_TYPE)
			{
				pPhyBRVertexExport = (IPhyBlendedRigidVertex *)pPhyVertExport;

				for(nBoneCount = 0; nBoneCount < pPhyBRVertexExport->GetNumberNodes(); nBoneCount++)
				{
					pMaxBone = pPhyBRVertexExport->GetNode(nBoneCount);

					if(IsBoneInList(vecMaxBones, pMaxBone) == false)
					{
//						LOG.Write("\n  %s (blended)", pMaxBone->GetName());
						vecMaxBones.push_back(pMaxBone);
					}
				}
			}
			else
			{
				pPhyRigidVertexExport = (IPhyRigidVertex *)pPhyVertExport;

				pMaxBone = pPhyRigidVertexExport->GetNode();

				if(IsBoneInList(vecMaxBones, pMaxBone) == false)
				{
//					LOG.Write("\n  %s (rigid)", pMaxBone->GetName());
					vecMaxBones.push_back(pMaxBone);
				}
			}

			pPhyObjExport->ReleaseVertexInterface(pPhyVertExport);
		}

		pPhyFloatingVertex = pPhyObjExport->GetFloatingVertexInterface(nCount);

		if(pPhyFloatingVertex)
		{
			for(nBoneCount = 0; nBoneCount < pPhyFloatingVertex->GetNumberNodes(); nBoneCount++)
			{
				pMaxBone = pPhyFloatingVertex->GetNode(nBoneCount);

				if(IsBoneInList(vecMaxBones, pMaxBone) == false)
				{
//					LOG.Write("\n  %s (floating)", pMaxBone->GetName());
					vecMaxBones.push_back(pMaxBone);
				}
			}

			pPhyObjExport->ReleaseVertexInterface(pPhyFloatingVertex);
		}
	}

	pPhyExport->ReleaseContextInterface(pPhyObjExport);
	pPhyModifier->ReleaseInterface(I_PHYINTERFACE, pPhyExport);

	return bOK;
}
// --[  Method  ]---------------------------------------------------------------
//
//  - Class     : CStravaganzaMaxTools
//
//  - prototype : int BuildPhysiqueData(INode* pMaxNode,
//										CObject* pObject,
//                                      std::vector<std::string>    &vecBoneNames,
//										std::vector<CBlendedVertex> &vecBlendedVertices)
//
//  - Purpose   : Builds the bone data for a given node. Returns the number
//                of bones processed (0 = failure).
//
// -----------------------------------------------------------------------------
int	CStravaganzaMaxTools::BuildPhysiqueData(INode* pMaxNode,
											CObject* pObject,
											std::vector<std::string>    &vecBoneNames,
											std::vector<CBlendedVertex> &vecBlendedVertices)

{
	int nCount       = 0;
	int nBoneCount   = 0;

	Modifier          *pPhyModifier  = NULL; // Physique modifier
	IPhysiqueExport   *pPhyExport    = NULL; // Physique export interface
	IPhyContextExport *pPhyObjExport = NULL; // Physique object export interface

	vecBoneNames.clear();
	vecBlendedVertices.clear();

	// Build bone list

	std::vector<INode*> vecMaxBones;

	if(!AddNodeBones(vecMaxBones, pMaxNode))
	{
		LOG.Write("\nWARNING - Error building node %s bone list", pMaxNode->GetName());
		return 0;
	}

	// Build bones name list

	for(nBoneCount = 0; nBoneCount < vecMaxBones.size(); nBoneCount++)
	{
		vecBoneNames.push_back(vecMaxBones[nBoneCount]->GetName());
	}

	// Get Physique modifier

	if(pPhyModifier = GetPhysiqueModifier(pMaxNode))
	{
		pPhyExport = (IPhysiqueExport *)pPhyModifier->GetInterface(I_PHYINTERFACE);

		if(pPhyExport == NULL)
		{
			LOG.Write("\nWARNING - Couldn't get Physique export interface.\nFailed with node %s.", pMaxNode->GetName());
			return 0;
		}
	}

	// Get physique object export interface

	pPhyObjExport = pPhyExport->GetContextInterface(pMaxNode);

	if(pPhyObjExport == NULL)
	{
		pPhyModifier->ReleaseInterface(I_PHYINTERFACE, pPhyExport);
		LOG.Write("\nWARNING - Unable to get physique context export.\nFailed with node %s.", pMaxNode->GetName());
		return 0;
	}

	// Convert to rigid for time independent vertex assignment
	// Allow blending to export multi-link assignments

	pPhyObjExport->ConvertToRigid(true);
	pPhyObjExport->AllowBlending(true);

	// Build deformable vertex list

	bool bOK = true;
	int  nBlendedCount = 0, nBlendedRigidCount = 0, nFloatingCount = 0;

	for(nCount = 0; nCount < pPhyObjExport->GetNumberVertices(); nCount++)
	{
		IPhyVertexExport       *pPhyVertExport;
		IPhyBlendedRigidVertex *pPhyBRVertexExport;
		IPhyRigidVertex        *pPhyRigidVertexExport;
		IPhyFloatingVertex	   *pPhyFloatingVertex;

		pPhyVertExport  = pPhyObjExport->GetVertexInterface(nCount);

		CBlendedVertex blendedVertex;
		float fTotalWeight   = 0.0f;
		bool  bFloatingBones = false;

		// Floating Vertex

		pPhyFloatingVertex = pPhyObjExport->GetFloatingVertexInterface(nCount);

		if(pPhyFloatingVertex)
		{
			bFloatingBones = true;

			CVector3 v3OffsetVector;
			float    fWeight;

			// More than one bone

			int nNumVtxBones = pPhyFloatingVertex->GetNumberNodes();

//			LOG.Write("\n%u - Floating, with %u bones", nCount, nNumVtxBones);

			for(nBoneCount = 0; nBoneCount < nNumVtxBones; nBoneCount++)
			{
				int nIndex = GetBoneIndex(vecMaxBones, pPhyFloatingVertex->GetNode(nBoneCount));

				if(nIndex == -1)
				{
					LOG.Write("\nWARNING - Unable to get bone index (%s)", pPhyFloatingVertex->GetNode(nBoneCount)->GetName());
					bOK = false;
					break;
				}

				float fTotal;

				v3OffsetVector = Point3ToVector3(pPhyFloatingVertex->GetOffsetVector(nBoneCount));
				fWeight        = pPhyFloatingVertex->GetWeight(nBoneCount, fTotal);
				fTotalWeight  += fWeight;//fTotal;
				//fWeight = fTotal;

//				LOG.Write("\n     Weight = %f (%s)", fWeight, pPhyFloatingVertex->GetNode(nBoneCount)->GetName());

				blendedVertex.AddLink(v3OffsetVector, nIndex, fWeight);
			}

//			LOG.Write("\n     Total = %f", fTotalWeight);

			if(!ARE_EQUAL(fTotalWeight, 1.0f))
			{
				LOG.Write("\n WARNING - Vertex %u has total weights %f", nCount, fTotalWeight);
			}

			nFloatingCount++;

			pPhyObjExport->ReleaseVertexInterface(pPhyFloatingVertex);
		}

		if(pPhyVertExport)
		{
			if(pPhyVertExport->GetVertexType() & BLENDED_TYPE)
			{
				CVector3 v3OffsetVector;
				float    fWeight;

				// More than one bone

				pPhyBRVertexExport = (IPhyBlendedRigidVertex *)pPhyVertExport;
				int nNumVtxBones   = pPhyBRVertexExport->GetNumberNodes();

//				LOG.Write("\n%u - Blended, with %u bones", nCount, nNumVtxBones);

				for(nBoneCount = 0; nBoneCount < nNumVtxBones; nBoneCount++)
				{
					int nIndex = GetBoneIndex(vecMaxBones, pPhyBRVertexExport->GetNode(nBoneCount));

					if(nIndex == -1)
					{
						LOG.Write("\nWARNING - Unable to get bone index (%s)", pPhyBRVertexExport->GetNode(nBoneCount)->GetName());
						bOK = false;
						break;
					}

					v3OffsetVector = Point3ToVector3(pPhyBRVertexExport->GetOffsetVector(nBoneCount));
					fWeight        = pPhyBRVertexExport->GetWeight(nBoneCount);
					fTotalWeight  += fWeight;

//					LOG.Write("\n     Weight = %f (%s)", fWeight, pPhyBRVertexExport->GetNode(nBoneCount)->GetName());

					blendedVertex.AddLink(v3OffsetVector, nIndex, fWeight);
				}

//				LOG.Write("\n     Total = %f", fTotalWeight);

				if(!ARE_EQUAL(fTotalWeight, 1.0f))
				{
					LOG.Write("\n WARNING - Vertex %u has total weights %f", nCount, fTotalWeight);
				}

				nBlendedCount++;
			}
			else
			{
				CVector3 v3OffsetVector;
				float    fWeight;

				// Single bone

				pPhyRigidVertexExport = (IPhyRigidVertex *)pPhyVertExport;

				int nIndex = GetBoneIndex(vecMaxBones, pPhyRigidVertexExport->GetNode());

				if(nIndex == -1)
				{
					LOG.Write("\nWARNING - Unable to get bone index (%s)", pPhyRigidVertexExport->GetNode()->GetName());
					bOK = false;
					break;
				}

				v3OffsetVector = Point3ToVector3(pPhyRigidVertexExport->GetOffsetVector());
				fWeight        = 1.0f;
				fTotalWeight   = 1.0f;

//				LOG.Write("\n%u - Rigid (%s)", nCount, pPhyRigidVertexExport->GetNode()->GetName());

				blendedVertex.AddLink(v3OffsetVector, nIndex, fWeight);

				nBlendedRigidCount++;
			}

			pPhyObjExport->ReleaseVertexInterface(pPhyVertExport);
		}

		for(int i = 0; i < blendedVertex.GetNumLinks(); i++)
		{
			// Normalize
			blendedVertex.SetWeight(i, blendedVertex.GetWeight(i) / fTotalWeight);
		}

		vecBlendedVertices.push_back(blendedVertex);
	}

	pPhyExport->ReleaseContextInterface(pPhyObjExport);
	pPhyModifier->ReleaseInterface(I_PHYINTERFACE, pPhyExport);

	if(!bOK)
	{
		vecMaxBones.clear();
		vecBoneNames.clear();
		vecBlendedVertices.clear();
	}
	else
	{
		LOG.Write("\nPhysique: %u bones, %u vertices (%u rigid, %u rigidblended, %u floating)",
				  vecBoneNames.size(),
				  vecBlendedVertices.size(),
				  nBlendedRigidCount,
				  nBlendedCount,
				  nFloatingCount);
	}

	return vecMaxBones.size();
}
void SGP_MaxInterface::GetBoneGroup( Modifier *pModifier,
									 int nModifierType, 
									 INode* pNode, 
									 Mesh* pMesh,
									 int nVertexId,
									 BoneGroup& boneGroup )
{
	if( !pMesh )
	{
		assert( false );
		return;
	}

	if( nVertexId >= pMesh->numVerts )
	{
		assert( false );
		return;
	}

	// static mesh
	if( nModifierType == MODIFIER_NONE )
	{
		INode* pParent = pNode->GetParentNode();
		if( pParent && ( IsBone( pParent ) || IsBipedBone( pParent ) ) )
		{
			Influence infl;
			infl.fWeight = 1.0f;
			strcpy( infl.szBoneName, pParent->GetName() );
			boneGroup.AddInfluence( infl );
		}
	}
	// check for physique modifier
	else if( nModifierType == MODIFIER_PHYSIQUE )
	{
		assert( pModifier && "get bone group error, modifier is null" );
		// create a physique export interface
		IPhysiqueExport *pPhysiqueExport = (IPhysiqueExport *)pModifier->GetInterface(I_PHYINTERFACE);
		if(pPhysiqueExport == NULL)
		{
			return;
		}

		// create a context export interface
		IPhyContextExport *pContextExport = (IPhyContextExport *)pPhysiqueExport->GetContextInterface(pNode);
		if(pContextExport == NULL)
		{
			pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);
			return;
		}

		// set the flags in the context export interface
		pContextExport->ConvertToRigid(TRUE);
		pContextExport->AllowBlending(TRUE);

		// get the vertex export interface
		IPhyVertexExport *pVertexExport = (IPhyVertexExport *)pContextExport->GetVertexInterface(nVertexId);
		if(pVertexExport == NULL)
		{
			pPhysiqueExport->ReleaseContextInterface(pContextExport);
			pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);
			return;
		}

		// get the vertex type
		int vertexType = pVertexExport->GetVertexType();

		// handle the specific vertex type
		if(vertexType == RIGID_TYPE)
		{
			// typecast to rigid vertex
			IPhyRigidVertex *pTypeVertex = (IPhyRigidVertex *)pVertexExport;


			Influence infl;
			if( pTypeVertex->GetNode() )
			{
				strcpy( infl.szBoneName, pTypeVertex->GetNode()->GetName() );
				infl.fWeight = 1.0f;
				boneGroup.AddInfluence( infl );
			}
			else 
				return;
		}
		else if(vertexType == RIGID_BLENDED_TYPE)
		{
			// typecast to blended vertex
			IPhyBlendedRigidVertex *pTypeVertex = (IPhyBlendedRigidVertex *)pVertexExport;

			// loop through all influencing bones
			Influence infl;
			for(int nodeId = 0; nodeId < pTypeVertex->GetNumberNodes(); nodeId++)
			{
				strcpy( infl.szBoneName, pTypeVertex->GetNode( nodeId )->GetName() );
				infl.fWeight = pTypeVertex->GetWeight( nodeId );

				boneGroup.AddInfluence( infl );
			}
		}

		// release all interfaces
		pPhysiqueExport->ReleaseContextInterface(pContextExport);
		pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);

	}
	else if( nModifierType == MODIFIER_SKIN)
	{
		assert( pModifier && "get bone group error, modifier is null" );
		// create a skin interface
		ISkin *pSkin = (ISkin*)pModifier->GetInterface(I_SKIN);
		if(pSkin == 0)
		{
			return;
		}

		// create a skin context data interface
		ISkinContextData *pSkinContextData;
		pSkinContextData = (ISkinContextData *)pSkin->GetContextInterface(pNode);
		if(pSkinContextData == NULL)
		{
			pModifier->ReleaseInterface(I_SKIN, pSkin);
			return;
		}

		// loop through all influencing bones
		for(int nodeId = 0; nodeId < pSkinContextData->GetNumAssignedBones(nVertexId); nodeId++)
		{
			// get the bone id
			int boneId = pSkinContextData->GetAssignedBone(nVertexId, nodeId);
			if(boneId < 0)
				continue;

			INode* pBone = pSkin->GetBone( boneId );
			Influence infl;
			strcpy( infl.szBoneName, pBone->GetName() );
			infl.fWeight = pSkinContextData->GetBoneWeight(nVertexId, nodeId);

			boneGroup.AddInfluence( infl );
		}

		// release all interfaces
		pModifier->ReleaseInterface(I_SKIN, pSkin);
	}
}