Interactive/MaterialSaveCustom.py

# (C) Copyright 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.

# You will need Autodesk Showcase 2008 or later.
#
 
#
# You will get a "Save To Custom Materials" menu item on the material
# in scene elements.  Choosing it will add that material's definition
# to the CustomMaterials.xml in your preferences/Materials directory.
# You will need to restart Showcase to see those materials in the
# material library.

__all__ = ['MaterialSaveCustom', 'instantiate']

from UserCustomization         import UserCustomBase, CustomInfo
from GenericPopupMenu          import GenericPopupMenuItem
from awSupportApi              import ApplicationSettings
from MessageInterpreter        import MessageInterpreter
from MaterialList              import MaterialListUiItem
from ParameterGroupIOUtilities import writeParameters
from XMLSaxData                import Data
from RtfXMLTags                import *
from MessageRegistry           import theMessageRegistry
from RTFapi                    import Node

import XMLSaxWriter, XMLSaxReader
import MaterialIO
import os

SAVE_TO_CUSTOM_MATERIAL_Doc = \
[(
"""
Save current materials in the scene as custom material.
"""
),
[[("Name"),("Menu label name")],
 ]
]

theMessageRegistry.register("SAVE_TO_CUSTOM_MATERIAL",
    (unicode,), 0, SAVE_TO_CUSTOM_MATERIAL_Doc)


class MaterialSave( UserCustomBase ):
    def __init__( self ):
        #UserCustomBase.__init__(self)
        self.__myInterpreter = LocalInterpreter()
        self.__myMaterialOnNode = None

    def getInterpreter( self, isInteractive ):
        if isInteractive:
            return self.__myInterpreter
        return None

    def appendMenuItemsNode(self,id,menu,nodeInfo):
        if "Smart" == id:
            (nodeId, nodeLabel, leaf, sel) = nodeInfo
            node = Node.getNodeById(nodeId)
            if node:
                mat = node.getAppearance().getMaterial()
                self.__myMaterialOnNode = mat.getName()

                menu.appendItem( _('Save Material to Custom Materials'),
                                 self.__onSaveMaterialOnNode )

    def appendPopupMenuItems( self, id, popupMenu, item ):
        if "MaterialSelector" == id:
            if isinstance( item, MaterialListUiItem ):
                menuItem = GenericPopupMenuItem(_( 'Save to Custom Materials'),
                                                self.__cbMaterialListUiItem,
                                                item )
                popupMenu.append( menuItem )

    def __writeToCustom( self, mat ):
        if self.__myInterpreter is not None:
            where = self.__myInterpreter.writeToCustom( mat )
            if where is not None:
                self.sendMessage( 'MATERIAL_LOAD_LIBRARY_PATHS', ((where,),) )
                pass

    def __onSaveMaterialOnNode( self, event ):
        self.__writeToCustom( self.__myMaterialOnNode )

    def __cbMaterialListUiItem( self, item ):
        self.__writeToCustom( item.name() )


def instantiate():
    return MaterialSave()


class LocalInterpreter( MessageInterpreter ):

    def __init__( self ):
        MessageInterpreter.__init__( self )
        self.__myDocument = None
        self.__myPrefsDir = ApplicationSettings.instance().getUserPreferencesDirectory()

    def SET_DOCUMENT( self, message ):
        ( self.__myDocument, ) = message.data

    def APPLICATION_CLOSE_SCENE( self, message ):
        self.__myDocument = None

    def getMaterials( self ):
        materials = []
        if self.__myDocument is not None:
            materials = self.__myDocument.get( MaterialIO.id )
        return materials

    def writeToCustom( self, name ):
        # Return the path to the XML file, or None
        materials = self.getMaterials()
        if materials is None:
            return None
        result = None
        if name in materials:
            material = materials[name]

            materialData = Data( kMaterialTag, {} )
            materialData.setAttribute( kMaterialNameAttr, material.getLabel() )
            materialData.setAttribute( kMaterialTypeAttr, material.getType() )
            writeParameters( materialData, material, None )

            dir = os.path.join( self.__myPrefsDir, "Materials" )
            if not os.path.isdir( dir ):
                try:
                    os.mkdir( dir )
                except:
                    print "Failed to create %s" % (dir)
            file = os.path.join( dir, "CustomMaterials.xml" )
            result = file
            hadFile = os.path.isfile(file)
            if hadFile:
                xmlRootData = XMLSaxReader.parse(file)

                materialGroupData = xmlRootData[kMaterialDescriptionTag]

                materialGroupData = xmlRootData[kMaterialCollectionTag][0]
            else:
                # If the file isn't there, we need to create it
                materialGroupData = Data( kMaterialCollectionTag, {} )
                materialGroupData.setAttribute( kMaterialCollectionNamespaceAttr,
                                                "Custom Materials" )

                xmlRootData = Data( kMaterialDescriptionTag, {} )
                xmlRootData.setAttribute( kAppVersion, "1.0" )
                xmlRootData.setAttribute( kMaterialFormatAttr, "1" )
                xmlRootData.addSubData( materialGroupData )

            if materialGroupData is not None:
                materialGroupData.addSubData( materialData )
                XMLSaxWriter.write( file, xmlRootData )
        return result

    def SAVE_TO_CUSTOM_MATERIAL( self, message ):
        ( name, ) = message.data
        where = self.writeToCustom( name )
        if where is not None:
            self.sendMessage( 'MATERIAL_LOAD_LIBRARY_PATHS', ( (where,), ) )


def info():
    customInfo = CustomInfo()
    customInfo.vendor = 'Autodesk'
    customInfo.version = '3.0'
    customInfo.api = '2013'
    customInfo.shortInfo = "Save the custom materials in the material library."
    customInfo.longInfo = \
"""Add a "Save To Custom Materials" menu item on the material of in-scene elements. Choosing it \
will add that material's definition to the CustomMaterials.xml in your preferences/Materials \
directory. You will need to restart Showcase to see those materials in the material library.
"""
    return customInfo