xmlAssembly/AssemblyExporter.cpp
 
 
 
xmlAssembly/AssemblyExporter.cpp
//-
//*************************************************************************/
// Copyright (C) 2012 Autodesk, Inc.  All rights reserved.
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy
// form.  
//**************************************************************************/
//+

#include "AssemblyExporter.h"

#include <map>
#include <string>

#include <maya/MString.h> 
#include <maya/MObjectArray.h>
#include <maya/MStringArray.h>
#include <maya/MDagPath.h>
#include <maya/MItDag.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnAssembly.h>
#include <maya/MGlobal.h>

#include "XMLAssetNode.h"
#include "XMLAssetInstance.h"
#include "AssetGuidManager.h"
#include "AssetLibManager.h"
#include "FnAssetInstance.h"
#include "FnAssetInstanceUtil.h"
#include "Representation.h"
#include "Utility.h"
#include "XMLReader.h"
#include "LocatorRep.h"
#include "XMLFileRep.h"
#include "ChildrenRep.h"
#include "XMLStrings.h"
#include "FileSystemManager.h"

AssemblyExporter::AssemblyExporter(void)
{
}

AssemblyExporter::~AssemblyExporter(void)
{
}

MStatus AssemblyExporter::exportScene(MString& fileName)
{
        MStatus status;

        // get all hierarchy root nodes         
        MObjectArray rootNodes;
        MItDag dagIterator( MItDag::kBreadthFirst, MFn::kAssembly, &status);
        for ( ; !dagIterator.isDone(); dagIterator.next() ) {
                // check if the hierarchy root node
                if (dagIterator.depth() > 1)
                        break;

                MDagPath dagPath;
                status = dagIterator.getPath(dagPath);
                MObject depNode = dagPath.node();                 
                if (FnAssetInstanceUtil::isAssetInstance(depNode))
                {
                        rootNodes.append(depNode);
                }
        }

        // if no assembly node is directly under the root, just return
        if (rootNodes.length() == 0)
        {
                return MS::kSuccess;
        }

    //Get resolved file path.
    MString resolvedFilename = theAssetLibMgr.getAbsoluteFilename( fileName);

        if (rootNodes.length() == 1)
        {
                saveAssetNode(rootNodes[0], resolvedFilename);
        }
        else
        {
                // if there are multiple assembly node directly under the world,
                // write a XML file for them
                XMLAssetNode assetNode;
        MString childFileName;
                for (unsigned int i = 0; i < rootNodes.length(); i++)
                {
                        MString childGuid = saveAssetNode(rootNodes[i], childFileName);
                        AssetInstance* instance = getInstanceInfo(rootNodes[i], childGuid);
                        assetNode.addInstance(instance);
                }

                MString guid = theGuidManager.genGuid();
                assetNode.setGuid(guid);

                MString libFile = theAssetLibMgr.getFirstLibFile();
                assetNode.setLibFile(libFile);
                // use file name as the asset name
                assetNode.setName(Utility::getRawFileName(fileName));

        //save the new created asset node to the lib file.
        AssetDefinition* assetDef = theAssetLibMgr.getFirstLib();
        assetDef->addAsset( guid, fileName);

                fWriter.writeAssetNode( &assetNode, resolvedFilename);
        }
        
        // update definition file, maybe we need to check if there are new asset
        theAssetLibMgr.saveLibFile();
    theAssetLibMgr.clearDef();

        return MS::kSuccess;
}

MString AssemblyExporter::saveAssetNode(const MObject &assemblyNode, const MString& fileName)
{
        XMLAssetNode assetNode;
    MFnDagNode dagNode(assemblyNode);
        MString guid = FnAssetInstance::getGUID(assemblyNode);
        if(guid == "")
                guid = theGuidManager.genGuid();
    
        MString assetFile = fileName;
    bool needToRegister = false;

    if( assetFile.length() > 0)
    {

        //check whether the assetFile has been already in the lib file.
        bool isAssetFileExisted = false;
        std::string fileNameStr = assetFile.asChar();
        AssetDefinition* assetDef = theAssetLibMgr.getFirstLib();
        const std::map<std::string, std::string>& componentMap = assetDef->getComponentMap();
        std::map<std::string, std::string>::const_iterator it = componentMap.begin();
        while( it != componentMap.end())
        {
            if( it->second == fileNameStr)
            {
                isAssetFileExisted = true;
                guid = it->first.c_str();
                break;
            }
            ++it;
        }

        if( !isAssetFileExisted)
        {
            needToRegister = true;
        }
    }
    else
    {
        //Now we don't have any valid filename, so just create one and register it later.
        assetFile = generateFileName( assemblyNode);
                needToRegister = true;
    }

        if( needToRegister)
    {
        // add the new file to asset library
        AssetDefinition* assetDef = theAssetLibMgr.getFirstLib();
        assetDef->addAsset( guid, assetFile);

        // update Guid manager, fill the file name
        theGuidManager.addItem( guid, assetFile, MObject::kNullObj);
        // update lib manager, connect the guid with lib file.
        theAssetLibMgr.addLibFile( guid, theAssetLibMgr.getFirstLibFile());
    }

        {
                int childrenAssetCount = 0;
                // write out child assembly nodes, get all sub-instance info
                for (unsigned int i = 0; i < dagNode.childCount(); i++)
                {
                        MObject childNode = dagNode.child(i);
                        if (FnAssetInstanceUtil::isAssetInstance(childNode))
                        {
                                childrenAssetCount++;
                                MString guid = FnAssetInstance::getGUID(childNode);
                                MString childFileName;
                                MStringArray fileArray;
                                theGuidManager.findPathByGuid( guid, fileArray);
                                if( fileArray.length() > 0)
                                {
                                        childFileName = fileArray[ 0];
                                }
                                MString childGuid = saveAssetNode(childNode, childFileName);
                assetNode.addInstance(getInstanceInfo(childNode, childGuid));
            }
                }

                //the children asset is not loaded to Maya, 
                //don't override current XML file since we don't want to lose the ability of switch rep.
                MStringArray repInfo;
                if ( FnAssetInstance::findRepInfo(assemblyNode, XMLStrings::kwChildren, repInfo) && childrenAssetCount == 0){
                        return guid;
                }
        }



        // fill all representation info
        fillRepInfo(assemblyNode, assetNode);

        assetNode.setGuid(guid);
    //If no lib file for this asset node, use the first lib as default.
    //So we don't register new lib definition for it.
    //TODO: Maybe in the future, we will create new definition.
    MString libFile = theAssetLibMgr.getFirstLibFile();
    //Save as relative path.
    
        assetNode.setLibFile( libFile);
        assetNode.setName( Utility::getRawFileName( assetFile));

        // write file out
        fWriter.writeAssetNode( &assetNode, assetFile); 

        return guid;
}

AssetInstance* AssemblyExporter::getInstanceInfo(const MObject& assemblyNode, const MString& guid)
{
        // The instance will be add to AssetNode. And destroyed in AssetNode
        XMLAssetInstance* instance = new XMLAssetInstance;
    
        instance->setVisibility(FnAssetInstance::getVisibility(assemblyNode));
        instance->setXform(FnAssetInstance::getXform(assemblyNode));

    MFnAssembly fnAssembly(assemblyNode);
    MString activeRep = fnAssembly.getActive();
    instance->setActiveRep(activeRep);

    MString instanceGuid = FnAssetInstance::getGUID(assemblyNode);
    instance->setGuid(instanceGuid);
    instance->setNodeGuid(guid);
        
        instance->setName(fnAssembly.name());

        return instance;
}

// Fill the representation list for AssetNode with the information of assembly node
void AssemblyExporter::fillRepInfo(const MObject &assemblyNode, AssetNode &assetNode)
{

    // add the new file to asset library
    AssetDefinition* assetDef = theAssetLibMgr.getFirstLib();
    
        // get all rep types
        MStringArray repInfos;
        FnAssetInstance::getRepInfo(assemblyNode, repInfos);
        for (unsigned int i = 0; i < repInfos.length()/4; i++)
        {
                MString repType = repInfos[i*4+Representation::kRepType];
                MString repName = repInfos[i*4+Representation::kRepName];
                
                // skip the locator rep, xmlFile rep and Children rep
                if ( repType == LocatorRep::type() || repType == XMLFileRep::type() || repType == ChildrenRep::type())
                        continue;

                // The representation will be add to AssetNode. And destroyed in AssetNode
                Representation* rep = new Representation;
        MString guid = repInfos[i*4+Representation::kRepGuid];
                rep->setNodeGuid( guid);
                rep->setName(repName);
                rep->setType(repType);

        if ( repName != XMLStrings::kwMayaScene || !assetNode.isLeafNode())
        {
            rep->setData(repInfos[i*4+Representation::kRepData]);
        }

        MStringArray repFileArray;
        theGuidManager.findPathByGuid( guid, repFileArray);
        MString repFile = repFileArray[0];
        if( repFile.length() > 0)
        {
            assetDef->addAsset( guid, repFile);
        }

                assetNode.addRepresentation(rep);               
        }
}

MString AssemblyExporter::generateFileName( const MObject& assemblyNode)
{
    MDagPath dagPath;
    MDagPath::getAPathTo(assemblyNode, dagPath);

    // set asset file name by node name
    MString path = dagPath.partialPathName();
    MStringArray sepPath;
    path.split('|', sepPath);
    MString filename = theAssetLibMgr.getXMLRootDir();
        filename += "/"+sepPath[sepPath.length()-1];
    filename += ".xml";
    return theAssetLibMgr.getAbsoluteFilename( filename);
}