// --------------------------------------------------------------------
    MStatus SceneGraph::addInstancedDagPaths ( MSelectionList& selectionList )
    {
        MStatus status;
        int length = selectionList.length ( &status );
        if ( status != MStatus::kSuccess ) return MStatus::kFailure;

        for ( int i=0; i<length; i++ )
        {
            MDagPath dagPath;
            if ( selectionList.getDagPath ( i, dagPath ) != MStatus::kSuccess ) return MStatus::kFailure;

            if ( dagPath.isInstanced() )
            {
                int includedInstance=dagPath.instanceNumber ( &status );
                if ( status != MStatus::kSuccess ) return MStatus::kFailure;

                MObject object=dagPath.node ( &status );
                if ( status != MStatus::kSuccess ) return MStatus::kFailure;

                MDagPathArray paths;
                if ( MDagPath::getAllPathsTo ( object, paths ) != MStatus::kSuccess ) return MStatus::kFailure;

                int numPaths=paths.length();
                for ( int p=0; p<numPaths; p++ )
                    if ( p!=includedInstance )
                        selectionList.add ( paths[p] );
            }
        }

        return MStatus::kSuccess;
    }
Ejemplo n.º 2
0
    // In order to correctly extract skinweights we first
    // need to go into bindpose. (and we need to remember the current
    // pose, to be able to undo it).
    //
    // I know: nearly no error-checking.
    void
    BindPoseTool::GoIntoBindPose()
    {
		MStatus status;
        std::cout << " Going into bindpose ";
        // == turn IK off
        // save current state
        undoInfo.ikSnap = MIkSystem::isGlobalSnap(&status);
        undoInfo.ikSolve = MIkSystem::isGlobalSolve();
        // turn it off
        MIkSystem::setGlobalSnap(false);
        MIkSystem::setGlobalSolve(false);

        // == put joints into bindPose
        for (MItDag dagIt(MItDag::kDepthFirst, MFn::kJoint);
             !dagIt.isDone();
             dagIt.next())
        {
            std::cout << ".";
            MDagPath jointPath;
            dagIt.getPath(jointPath);
            if (jointPath.isInstanced())
            { // we only work on the first instance of an instanced joint.
                if (jointPath.instanceNumber() != 0)
                    continue;
            }

            BindPoseUndoInformation::JointMatrixVector::value_type joint2transform;

            MFnIkJoint joint(jointPath.node());

            MTransformationMatrix currentTransform = joint.transformation(&status);

            joint2transform.first = jointPath.node();
            joint2transform.second = currentTransform;
            undoInfo.savedTransforms.push_back(joint2transform);

            MMatrix bindPoseMatrix = getBindPoseMatrix(joint);
            joint.set(bindPoseMatrix);
        }
        std::cout << std::endl;

        //cout << "bindPose end" << endl;
        // don't know, if this is really necessary, but MS xporttranslator
        // does it...
        syncMeshes();

	// remember the change
	inBindPose = true;
    }
    //---------------------------------------------------------------
    void LightExporter::exportLights ( SceneElement* sceneElement )
    {
        // If we have a external reference, we don't need to export the data here.
        if ( !sceneElement->getIsLocal() ) return;
        if ( !sceneElement->getIsExportNode () ) return;

        // Check if it is a light.
        SceneElement::Type sceneElementType = sceneElement->getType();
        if ( sceneElementType == SceneElement::LIGHT )
        {
            // Get the current dag path
            MDagPath dagPath = sceneElement->getPath();

            // Check if the current scene element isn't already exported.
            SceneGraph* sceneGraph = mDocumentExporter->getSceneGraph();
            if ( sceneGraph->findExportedElement ( dagPath ) ) return;

            // Check if the current element is an instance. 
            // We don't need to export instances, because we export the original instanced element.
            bool isInstance = ( dagPath.isInstanced() && dagPath.instanceNumber() > 0 );

            // If the original instanced element isn't already exported, we have to export it now.
            if ( isInstance )
            {
                // Get the original instanced element.
                MDagPath instancedPath;
                dagPath.getPath ( instancedPath, 0 );

                // Check if the original instanced element is already exported.
                SceneGraph* sceneGraph = mDocumentExporter->getSceneGraph();
                SceneElement* exportedElement = sceneGraph->findExportedElement ( instancedPath );
				if (exportedElement == 0)
				{
					// Export the original instanced element and push it in the exported scene graph. 
					if (exportLight(instancedPath))
					{
						SceneElement* instancedSceneElement = sceneGraph->findElement(instancedPath);
						SceneGraph* sceneGraph = mDocumentExporter->getSceneGraph();
						sceneGraph->addExportedElement(instancedSceneElement);
					}
				}
            }
            else
            {
                // Export the element and push it in the exported scene graph. 
                if ( exportLight ( dagPath ) )
                {
                    SceneGraph* sceneGraph = mDocumentExporter->getSceneGraph();
                    sceneGraph->addExportedElement( sceneElement );
                }
            }
        }


        // Recursive call for all the child elements
        for ( uint i=0; i<sceneElement->getChildCount(); ++i )
        {
            SceneElement* childElement = sceneElement->getChild ( i );
            exportLights ( childElement );
        }
    }
    // --------------------------------------------------------------------
    bool SceneGraph::getIsExportNode ( 
        const MDagPath& dagPath, 
        bool& isForced,
        bool& isVisible )
    {
        // Does this dagPath already exist? If so, only recurse if FollowInstancedChildren() is set.
        MFnDagNode dagFn ( dagPath );
        String dagNodeName = dagFn.name().asChar();

        bool isSceneRoot = dagPath.length() == 0;

        // Ignore default and intermediate nodes (history items)
        bool isIntermediateObject = dagFn.isIntermediateObject();
        if ( ( dagFn.isDefaultNode() && !isSceneRoot ) || isIntermediateObject )
        {
            return false;
        }

        MString nodeName = dagPath.partialPathName();
        if ( nodeName == MString ( NIMA_INTERNAL_PHYSIKS ) )
        {
            // Skip this node, which is only used
            // by Nima as a work-around for a Maya bug.
            return false;
        }

        // If we are not already forcing this node, its children
        // check whether we should be forcing it (skinning of hidden joints).
        isForced = isForcedNode ( dagPath );
        DagHelper::getPlugValue ( dagPath.node(), ATTR_VISIBILITY, isVisible );
        bool isInstanced = dagPath.isInstanced();
        uint instanceNumber = dagPath.instanceNumber();

        if ( !isForced )
        {
            // Check for visibility
            if ( !ExportOptions::exportInvisibleNodes() && !isVisible )
            {
                // Check if the visibility of the element is animated.
                AnimationSampleCache* animationCache = mDocumentExporter->getAnimationCache();
                if ( !AnimationHelper::isAnimated ( animationCache, dagPath.node(), ATTR_VISIBILITY ) )
                {
                    return false;
                }
            }
            else if ( !isVisible && !ExportOptions::exportDefaultCameras() )
            {
                // Check for the default camera transform names.
                if ( nodeName == CAMERA_PERSP || nodeName == CAMERA_TOP || nodeName == CAMERA_SIDE || nodeName == CAMERA_FRONT ||
                     nodeName == CAMERA_PERSP_SHAPE || nodeName == CAMERA_TOP_SHAPE || nodeName == CAMERA_SIDE_SHAPE || nodeName == CAMERA_FRONT_SHAPE )
                    return false;
            }
        }

        isForced &= !isVisible;
        if ( !isForced )
        {
            // We don't want to process manipulators
            if ( dagPath.hasFn ( MFn::kManipulator ) || dagPath.hasFn ( MFn::kViewManip ) ) return false;

            // Check for constraints which are not exported
            //if ( !ExportOptions::exportConstraints() && dagPath.hasFn ( MFn::kConstraint ) ) return false;
            if ( dagPath.hasFn ( MFn::kConstraint ) ) return false;

            // Check set membership exclusion/inclusion
            if ( SetHelper::isExcluded ( dagPath ) ) return false;
        }

        return true;
    }
    // ------------------------------------------------------------
    bool SceneGraph::create ( bool selectionOnly )
    {
        // The flag, if just the selected elements should be exported.
        mExportSelectedOnly = selectionOnly;

        // Add all the animation expressions
        MItDependencyNodes depIter ( MFn::kExpression );
        for ( ; !depIter.isDone(); depIter.next() )
        {
            MObject item = depIter.item();
            if ( item.hasFn ( MFn::kExpression ) )
            {
                mAnimationExpressions.append ( item );
            }
        }

        // Push all nodes from root down to all meshes which have to be exported in a list.
        findForcedNodes ();

        // Fills the list with all root targets to export.
        bool success = retrieveExportNodes ();
        
        // Create a selection list containing only the root nodes (implies export all!)
        uint length = mTargets.length();
        for ( uint i=0; i<mTargets.length(); ++i )
        {
            MDagPath dagPath;
            MStatus status = mTargets.getDagPath ( i, dagPath );
            if ( status != MStatus::kSuccess ) return false;

            // This node has no transform - i.e., it's the world node
            MObject transformNode = dagPath.transform ( &status );
            if ( !status && status.statusCode () == MStatus::kInvalidParameter ) continue;

            // Check if it is a valid transform node
            MFnDagNode transform ( transformNode, &status );
            if ( !status )
            {
                status.perror ( "MFnDagNode constructor" );
                return false;
            }

            // Create a new scene element
            SceneElement* sceneElement = createSceneElement ( dagPath );

            // Push the root nodes into the tree.
            // If the root node is instanced, push it at the beginning (no instance on root!).
            if ( dagPath.isInstanced () )
                mExportNodesTree.insert ( mExportNodesTree.begin (), sceneElement );
            else
                mExportNodesTree.push_back ( sceneElement );
            
            // Create the child elements
            //if ( sceneElement->getIsExportNode() )
            {
                createChildSceneElements ( sceneElement );
            }
        }

        return true;
    }
MStatus findTexturesPerPolygon::doIt( const MArgList& )
//
//  Description:
//      Find the texture files that apply to the color of each polygon of
//      a selected shape if the shape has its polygons organized into sets.
//
{
    // Get the selection and choose the first path on the selection list.
    //
	MStatus status;
	MDagPath path;
	MObject cmp;
	MSelectionList slist;
	MGlobal::getActiveSelectionList(slist);
	slist.getDagPath(0, path, cmp);

	// Have to make the path include the shape below it so that
	// we can determine if the underlying shape node is instanced.
	// By default, dag paths only include transform nodes.
	//
	path.extendToShape();

	// If the shape is instanced then we need to determine which
	// instance this path refers to.
	//
	int instanceNum = 0;
	if (path.isInstanced())
		instanceNum = path.instanceNumber();

    // Get a list of all sets pertaining to the selected shape and the
    // members of those sets.
    //
	MFnMesh fnMesh(path);
	MObjectArray sets;
	MObjectArray comps;
	if (!fnMesh.getConnectedSetsAndMembers(instanceNum, sets, comps, true))
		cerr << "ERROR: MFnMesh::getConnectedSetsAndMembers\n";

	// Loop through all the sets.  If the set is a polygonal set, find the
    // shader attached to the and print out the texture file name for the
    // set along with the polygons in the set.
	//
	for ( unsigned i=0; i<sets.length(); i++ ) {
		MObject set = sets[i];
		MObject comp = comps[i];

		MFnSet fnSet( set, &status );
		if (status == MS::kFailure) {
            cerr << "ERROR: MFnSet::MFnSet\n";
            continue;
        }

        // Make sure the set is a polygonal set.  If not, continue.
		MItMeshPolygon piter(path, comp, &status);
		if ((status == MS::kFailure) || comp.isNull())
            continue;

		// Find the texture that is applied to this set.  First, get the
		// shading node connected to the set.  Then, if there is an input
		// attribute called "color", search upstream from it for a texture
		// file node.
        //
		MObject shaderNode = findShader(set);
		if (shaderNode == MObject::kNullObj)
			continue;

		MPlug colorPlug = MFnDependencyNode(shaderNode).findPlug("color", &status);
		if (status == MS::kFailure)
			continue;

		MItDependencyGraph dgIt(colorPlug, MFn::kFileTexture,
						   MItDependencyGraph::kUpstream, 
						   MItDependencyGraph::kBreadthFirst,
						   MItDependencyGraph::kNodeLevel, 
						   &status);

		if (status == MS::kFailure)
			continue;
		
		dgIt.disablePruningOnFilter();

        // If no texture file node was found, just continue.
        //
		if (dgIt.isDone())
			continue;
		  
        // Print out the texture node name and texture file that it references.
        //
		MObject textureNode = dgIt.thisNode();
        MPlug filenamePlug = MFnDependencyNode(textureNode).findPlug("fileTextureName");
        MString textureName;
        filenamePlug.getValue(textureName);
		cerr << "Set: " << fnSet.name() << endl;
        cerr << "Texture Node Name: " << MFnDependencyNode(textureNode).name() << endl;
		cerr << "Texture File Name: " << textureName.asChar() << endl;
        
        // Print out the set of polygons that are contained in the current set.
        //
		for ( ; !piter.isDone(); piter.next() )
			cerr << "    poly component: " << piter.index() << endl;
	}

	return MS::kSuccess;
}
// Get shading engine
//
void CScriptedShapeTranslator::GetShapeInstanceShader(MDagPath& dagPath, MFnDependencyNode &shadingEngineNode)
{
   // Get instance shadingEngine
   shadingEngineNode.setObject(MObject::kNullObj);
   
   // First try the usual way
   MPlug shadingGroupPlug = GetNodeShadingGroup(dagPath.node(), (dagPath.isInstanced() ? dagPath.instanceNumber() : 0));
   if (!shadingGroupPlug.isNull())
   {
      shadingEngineNode.setObject(shadingGroupPlug.node());
      return;
   }
   
   char buffer[64];
   
   // Check connection from any shadingEngine on shape
   MStringArray connections;
   MGlobal::executeCommand("listConnections -s 1 -d 0 -c 1 -type shadingEngine "+dagPath.fullPathName(), connections);
   
   MSelectionList sl;
   
   if (connections.length() == 0)
   {
      // Check for direct surface shader connection
      MGlobal::executeCommand("listConnections -s 1 -d 0 -c 1 "+dagPath.fullPathName(), connections);
      
      for (unsigned int cidx=0; cidx<connections.length(); cidx+=2)
      {
         MString srcNode = connections[cidx+1];
         // Get node classification, if can find arnold/shader/surface -> got it
         MStringArray rv;
         MGlobal::executeCommand("getClassification `nodeType "+srcNode+"`", rv);
         if (rv.length() > 0 && rv[0].indexW("arnold/shader/surface") != -1)
         {
            connections.clear();
            
            MGlobal::executeCommand("listConnections -s 0 -d 1 -c 1 -type shadingEngine "+srcNode, connections);
            
            if (connections.length() == 2)
            {
               sl.add(connections[1]);
            }
            
            break;
         }
      }
   }
   else if (connections.length() == 2)
   {
      // Single connection, use same shader for all instances
      sl.add(connections[1]);
   }
   else if (connections.length() > 2)
   {
      // Many connections, expects the destination plug in shape to be an array
      // Use instance number as logical index, if this fails, use first shadingEngine in list
      
      bool found = false;
      
      sprintf(buffer, "[%d]", dagPath.instanceNumber());
      MString iidx = buffer;
      
      for (unsigned int cidx = 0; cidx < connections.length(); cidx += 2)
      {
         MString conn = connections[cidx];
         
         if (conn.length() < iidx.length())
         {
            continue;
         }
         
         if (conn.substring(conn.length() - iidx.length(), conn.length() - 1) != iidx)
         {
            continue;
         }
         
         sl.add(connections[cidx+1]);
         
         found = true;
         
         break;
      }
      
      if (!found)
      {
         MGlobal::displayWarning("[mtoaScriptedTranslators] Instance shader plug not found, use first found shadingEngine \"" + connections[1] + "\"");
         sl.add(connections[1]);
      }
   }
   
   if (sl.length() == 1)
   {
      MObject shadingEngineObj;
      
      if (sl.getDependNode(0, shadingEngineObj) == MS::kSuccess && shadingEngineObj.apiType() == MFn::kShadingEngine)
      {
         shadingEngineNode.setObject(shadingEngineObj);
      }
      else
      {
         if (shadingEngineObj != MObject::kNullObj)
         {
            MFnDependencyNode dn(shadingEngineObj);
            MGlobal::displayWarning("[mtoaScriptedTranslators] Not a shading engine \"" + dn.name() + "\"");
         }
      }
   }
}
Ejemplo n.º 8
0
MStatus CXRayObjectExport::ExportPart(CEditableObject* O, MDagPath& mdagPath, MObject&  mComponent)
{
	MStatus stat = MS::kSuccess;
	MSpace::Space space = MSpace::kWorld;

	MFnMesh fnMesh( mdagPath, &stat );
	if ( MS::kSuccess != stat) {
		fprintf(stderr,"Failure in MFnMesh initialization.\n");
		return MS::kFailure;
	}

	MString mdagPathNodeName = fnMesh.name();

	MFnDagNode dagNode1(mdagPath);
	u32 pc = dagNode1.parentCount();
	for(u32 ip=0;ip<pc;++ip)
	{
		MObject object_parent = dagNode1.parent(ip, &stat);
		if(object_parent.hasFn(MFn::kTransform))
		{
			MFnTransform parent_transform(object_parent,&stat);
	
			if ( MS::kSuccess == stat) 
			{
				mdagPathNodeName = parent_transform.name();
				break;
			}
		}
	}

	MItMeshPolygon meshPoly( mdagPath, mComponent, &stat );
	if ( MS::kSuccess != stat) {
		fprintf(stderr,"Failure in MItMeshPolygon initialization.\n");
		return MS::kFailure;
	}

	MItMeshVertex vtxIter( mdagPath, mComponent, &stat );
	if ( MS::kSuccess != stat) {
		fprintf(stderr,"Failure in MItMeshVertex initialization.\n");
		return MS::kFailure;
	}

	// If the shape is instanced then we need to determine which
	// instance this path refers to.
	//
	int instanceNum = 0;
	if (mdagPath.isInstanced())
		instanceNum = mdagPath.instanceNumber();

	// Get a list of all shaders attached to this mesh
	MObjectArray rgShaders;
	MIntArray texMap;
	MStatus status;
	status = fnMesh.getConnectedShaders (instanceNum, rgShaders, texMap);
	if (status == MStatus::kFailure)
	{
		Log("! Unable to load shaders for mesh");
		return (MStatus::kFailure);
	}

	XRShaderDataVec xr_data;
	{
		for ( int i=0; i<(int)rgShaders.length(); i++ ) {
			MObject shader = rgShaders[i];

			xr_data.push_back(SXRShaderData());
			SXRShaderData& D = xr_data.back();

			status = parseShader(shader, D);
			if (status == MStatus::kFailure) {
				status.perror ("Unable to retrieve filename of texture");
				continue;
			}
		}
	}

	CEditableMesh* MESH = new CEditableMesh(O);
	MESH->SetName(mdagPathNodeName.asChar());
	O->AppendMesh(MESH);

	int objectIdx, length;

	// Find i such that objectGroupsTablePtr[i] corresponds to the
	// object node pointed to by mdagPath
	length = objectNodeNamesArray.length();
	{
		for( int i=0; i<length; i++ ) {
			if( objectNodeNamesArray[i] == mdagPathNodeName ) {
				objectIdx = i;
				break;
			}
		}
	}
	// Reserve uv table
	{
		VMapVec& _vmaps	= MESH->m_VMaps;
		_vmaps.resize	(1);
		st_VMap*& VM	= _vmaps.back();
		VM				= new st_VMap("Texture",vmtUV,false);
	}

	// write faces
	{
		using FaceVec = xr_vector<st_Face>;
		using FaceIt = FaceVec::iterator;

		VMapVec& _vmaps			= MESH->m_VMaps;
		SurfFaces& _surf_faces	= MESH->m_SurfFaces;
		VMRefsVec& _vmrefs		= MESH->m_VMRefs;
		
		// temp variables
		FvectorVec	_points;
		FaceVec _faces;
		U32Vec _sgs;

		int f_cnt				= fnMesh.numPolygons();

		_sgs.reserve	(f_cnt);
		_faces.reserve	(f_cnt);
		_vmrefs.reserve	(f_cnt*3);

//		int lastSmoothingGroup = INITIALIZE_SMOOTHING;
		MPointArray rgpt;
		MIntArray rgint;

		PtLookupMap ptMap;
		CSurface* surf	= 0;
		for ( ; !meshPoly.isDone(); meshPoly.next()){
			// Write out the smoothing group that this polygon belongs to
			// We only write out the smoothing group if it is different
			// from the last polygon.
			//
			int compIdx	= meshPoly.index();
			int smoothingGroup = polySmoothingGroups[ compIdx ];
			// for each polygon, first setup the reverse mapping
			// between object-relative vertex indices and face-relative
			// vertex indices
			ptMap.clear();
			for (int i=0; i<(int)meshPoly.polygonVertexCount(); i++)
				ptMap.insert (PtLookupMap::value_type(meshPoly.vertexIndex(i), i) );

			// verify polygon zero area
			if (meshPoly.zeroArea()){
				status = MS::kFailure;
				Log("! polygon have zero area:",meshPoly.index());
				return status;
			}

			// verify polygon zero UV area
/*			if (meshPoly.zeroUVArea()){
				status = MS::kFailure;
				Log("! polygon have zero UV area:",meshPoly.index());
				return status;
			}
*/
			// verify polygon has UV information
			if (!meshPoly.hasUVs (&status)) {
				status = MS::kFailure;
				Log("! polygon is missing UV information:",meshPoly.index());
				return status;
			}

			int cTri;
			// now iterate through each triangle on this polygon and create a triangle object in our list
			status = meshPoly.numTriangles (cTri);	
			if (!status) {
				Log("! can't getting triangle count");
				return status;
			}

			for (int i=0; i < cTri; i++) {

				// for each triangle, first get the triangle data
				rgpt.clear();//triangle vertices
				rgint.clear();//triangle vertex indices 

				// triangles that come from object are retrieved in world space
				status = meshPoly.getTriangle (i, rgpt, rgint, MSpace::kWorld);

				if (!status) {
					Log("! can't getting triangle for mesh poly");
					return status;
				}

				if ((rgpt.length() != 3) || (rgint.length() != 3)) {
					Msg("! 3 points not returned for triangle");
					return MS::kFailure;
				}

				// Write out vertex/uv index information
				//
				R_ASSERT2(fnMesh.numUVs()>0,"Can't find uvmaps.");
				_faces.push_back(st_Face());
				_sgs.push_back(smoothingGroup);
				//set_smooth
				set_smoth_flags( _sgs.back(), rgint );

				st_Face& f_it		= _faces.back();
				for ( int vtx=0; vtx<3; vtx++ ) {
					// get face-relative vertex
					PtLookupMap::iterator mapIt;

					int vtLocal, vtUV;
					int vt = rgint[vtx];
					mapIt = ptMap.find(vt);
					Fvector2 uv;
					if (mapIt == ptMap.end()){
						Msg("! Can't find local index.");
						return MS::kFailure;
					}
					vtLocal = (*mapIt).second;

					status = meshPoly.getUVIndex (vtLocal, vtUV, uv.x, uv.y); 
					if (!status) {
						Msg("! error getting UV Index for local vertex '%d' and object vertex '%d'",vtLocal,vt);
						return status;
					}

					// flip v-part 
					uv.y=1.f-uv.y;

					f_it.pv[2-vtx].pindex	= AppendVertex(_points,rgpt[vtx]);
					f_it.pv[2-vtx].vmref	= _vmrefs.size();
					_vmrefs.push_back		(st_VMapPtLst());
					st_VMapPtLst& vm_lst	= _vmrefs.back();
					vm_lst.count			= 1;
					vm_lst.pts				= xr_alloc<st_VMapPt>(vm_lst.count);
					vm_lst.pts[0].vmap_index= 0;
					vm_lst.pts[0].index 	= AppendUV(_vmaps.back(),uv);
				}
				// out face material
				int iTexture	= texMap[meshPoly.index()];
				if (iTexture<0)
					xrDebug::Fatal(DEBUG_INFO,"Can't find material for polygon: %d",meshPoly.index());
				SXRShaderData& D= xr_data[iTexture];

				int compIdx = meshPoly.index();
				surf		= MESH->Parent()->CreateSurface(getMaterialName(mdagPath, compIdx, objectIdx),D);
				if (!surf)	return MStatus::kFailure;	
				_surf_faces[surf].push_back(_faces.size()-1);
			}
		}
		{
			// copy from temp
			MESH->m_VertCount	= _points.size();
			MESH->m_FaceCount	= _faces.size();
			MESH->m_Vertices	= xr_alloc<Fvector>(MESH->m_VertCount);
			Memory.mem_copy		(MESH->m_Vertices,&*_points.begin(),MESH->m_VertCount*sizeof(Fvector));
			MESH->m_Faces		= xr_alloc<st_Face>(MESH->m_FaceCount);
			Memory.mem_copy		(MESH->m_Faces,&*_faces.begin(),MESH->m_FaceCount*sizeof(st_Face));
			MESH->m_SmoothGroups = xr_alloc<u32>(MESH->m_FaceCount);
			Memory.mem_copy		(MESH->m_SmoothGroups,&*_sgs.begin(),MESH->m_FaceCount*sizeof(u32));

			MESH->RecomputeBBox	();
		}
		if ((MESH->GetVertexCount()<4)||(MESH->GetFaceCount()<2))
		{
			Log		("! Invalid mesh: '%s'. Faces<2 or Verts<4",*MESH->Name());
			return MS::kFailure;
		}
	}
	return stat;
}