TextureMonkey v1.0

Well, here is TextureMonkey V1.0 with the Alienbrain integration. If anybody ends up using it I'd like to hear about how it goes. Also, I'm interested in hearing about the way you integrate it into Photoshop. I integrated it using a javascipt and a call from a batch file, but I'm sure there is a more streamlined way of doing it.

In order to get it set up, make sure you fill in the variables up top with your project specifics. These are all called throughout the script to keep it tidy.

And here it is:
##############################################################################
#
# Photoshop texture exporter v1.0
# (C) Pete Hanshaw, 2012
# http://peterhanshawart.blogspot.com.au/
# Inspired by recursive Photoshop layer export script by
# Adam Pletcher
# http://techarttiki.blogspot.com.au/
#
##############################################################################
#
# Checks to see if a PSD file is open.
# For the currently active file, exports various 24-bit TGA textures based on
# layer groups found in the PSD.
#
# If Alienbrain is present attempts to check out the export files if required
# Does not check out source file- this should be left to the artists to ensure
# they are working with the latest revision PSD before modifying it.
#
# Requires the Win32 Extensions:
# http://python.net/crew/mhammond/win32/
#
##############################################################################
 
#Import required modules
from win32com.client.dynamic import Dispatch
from stat import *
import os
from sys import exit
import pythoncom
 
pythoncom.OleInitialize()

#Alienbrain variables
ab_project = "yourProjectHere"
ab_server = "alienbrainServerHere"

#Login name
ab_user_name = "yourUserNameHere" 

#Alienbrain workspace
abWorkspace = "\\\\Workspace\\yourProjectHere\\"

#the relative paths we want to export to
sourceTarget = "art\\source\\textures"
assetTarget = "art\\assets\\textures"
exportTarget = "art\\exports\\textures"

#project HDD location
projectRoot = "Z:\\yourProjectHere\\"

#map layer group names to export names. Currently includes:
#Diffuse, Normal, Specular, Illuminance, Gloss, alpha
exportTypes = {'d': '_d', 'n': '_n', 's': '_s', 'i' : '_i', 'g': '_g', 'a':'_a'}


#name check function
def name_check(name_to_check, exportTypes):
    """Checks the PSD name for bad naming convention.

    Specifically, makes sure that if an artist has already included _d or _a etc 
    on the end of the source file name, it does not get included in the final 
    export output name.
    """

    #create a variable containing the raw name
    doc_name = name_to_check
    
    #Create a test case to test against the PSD name that has the last two 
    #characters removed
    test_name = doc_name[:-2]

    #for each of the different conventions that an artist may have used...
    for key, name in exportTypes.items():

        #Create a test name to match against the psd name
        test_case = test_name + name

        #match them against each other. If we have a match, remove whatever the
        #additional two characters were and return a clean doc name
        if test_case == doc_name:
            doc_name = doc_name[:-2]
            return doc_name
            break
            
    #If we did not get a match in the for loop we can pass through an 
    #unmodified doc_name 
    return doc_name    


#Function to check permissions of files
def check_permissions(abWorkspace, project_path, relative_path, doc_name, layer_name):
    """Checks to see if an asset file is writable.
    
    If it is not writable, attempts to check it out from alienbrain.
    """
    check_path = project_path + relative_path + "\\" + doc_name + layer_name + ".tga"
        
    if os.access(check_path, os.W_OK):
        print "Files writable"
        return
    else:
        print "\n", check_path, "is not writable."
        print "Monkey wants to check that out for you!"
        #set the base path for the exports
        as_base_path = abWorkspace + relative_path + "\\" + doc_name + exportTypes[lsName] + ".tga"
        print "Checking out ", as_base_path

        com_param.Reset()
        com_param.Command = "CheckOut"
        com_param.SetParamIn("ShowDialog", "0")
       
        com_nxn.RunCommand ( as_base_path, com_param.Command, com_param.xml )

        #check the writability of the file again
        if os.access(check_path, os.W_OK):
            print "Check out successful"
            return
            

        else:
            os.system("color 0c")
            print "Checking out ", as_base_path, " failed"
            print "Please check out the export files manually"
            doc.ActiveHistoryState = saved_state            
            os.system("PAUSE")
            exit(0)

#Begin the script            
if (__name__ == '__main__'):
    #COM dispatch for Photoshop
    try:
        psApp = Dispatch('Photoshop.Application')
 
    except:
        os.system("color 0c")
        print "OOPS! Something went wrong..."
        print "The dispatch to Photoshop did not work"
        print "Texture monkey hides in shame..."
        os.system("PAUSE")
        exit(0)

    #attempt to establish a connection to the AB server
    com_nxn = Dispatch("NxNNamespace.NxNNamespaceHelper")
    com_param = Dispatch("NxNXMLHelper.NxNXMLParameter" )
    com_param.Command = "ProjectLoadEx"
    com_param.SetParamIn(ab_user_name, ab_project)
    com_param.SetParamIn(ab_server, "alienbrainServer")

    # Define 24bit Targa save options
    options = Dispatch('Photoshop.TargaSaveOptions')
    PsTargaBitsPerPixels = 24
    options.Resolution = PsTargaBitsPerPixels
    options.AlphaChannels = False
    options.RLECompression = False

    # Define 32bit Targa save options
    optionsAlpha = Dispatch('Photoshop.TargaSaveOptions')
    PsTargaBitsPerPixels = 32
    optionsAlpha.Resolution = PsTargaBitsPerPixels
    optionsAlpha.AlphaChannels = True
    optionsAlpha.RLECompression = False
 
    # Define PSD save options
    psdOptions = Dispatch('Photoshop.PhotoshopSaveOptions')
    psdOptions.annotations = False
    psdOptions.alphaChannels = True
    psdOptions.layers = True
    psdOptions.spotColors = True
    psdOptions.embedColorProfile = True
 
    #Get the currently active document
    try:
        doc = psApp.activeDocument
 
    #Gracefully end if a document is not open
    except:
        os.system("color 0c")
        print "OOPS! Something went wrong..."
        print "You need to have an active Photoshop Doc to use this script."
        os.system("PAUSE")
        exit(0)
 
    #Get the document name and strip it's extensionprojectRoot
    sourceFile = projectRoot + sourceTarget + '\\' + doc.name
    doc_name = os.path.splitext(doc.name)[0]

    #Change the names to lower case
    sourceFile = sourceFile.lower()
    doc_name = doc_name.lower()
    
    #check to see if the source file can be saved
    try:
        doc.SaveAs(sourceFile, psdOptions)
        print "Saving original doc", sourceFile
    except Exception:
        os.system("color 0c")
        print "\nThe PSD is not writable!\n"
        print "Monkey doesn't like messing with source files!"
        print "\nManually check out ", sourceFile," and try again!\n"
        os.system("PAUSE")
        exit(0)

    #Assuming the PSD is writable, lets go ahead and initialise alienbrain. 
    print "Monkey wants to load AlienBrain"
    tmp = com_nxn.RunCommand ( abWorkspace, com_param.Command, com_param.xml )
    if ( com_param.WasSuccessful ):
        print "SUCCESS: Alienbrain loaded."
    else:
        print "ERROR: Project failed to load"
        print "TextureMonkey won't be able to check out files for you!"
    
    #Check the PSD name for redundant extensions
    doc_name = name_check(doc_name, exportTypes)
    
    print "Exporting from ", sourceFile
    #print "Using base export name ", doc_name
    
    #Get our layer sets from the currently open doc
    layerSets = doc.LayerSets
 
    #Check if there are any layerSets in the current doc
    if (len(layerSets) > 0):
        # first hide all root-level layers
        for layer in doc.Layers:
            layer.Visible = False
 
        # ... and layerSets
        for layerSet in layerSets:
            layerSet.Visible = False
 
        # Loop through each LayerSet (aka Group)
        for layerSet in layerSets:
            lsName = layerSet.Name.lower()
 
            #save the current setup
            saved_state = doc.activeHistoryState
 
            if (lsName in exportTypes):
                layerSet.Visible = True # Make the group visible

                #make the asset and export file names
                exportFile = projectRoot + exportTarget + '\\' + doc_name + exportTypes[lsName] + '.tga'
                assetFile = projectRoot + assetTarget + '\\' + doc_name + exportTypes[lsName] + '.tga'
                
                #See if the export files are writable. If not, they are probably
                #not checked out!
                if (os.path.exists(exportFile)):
                    check_permissions(abWorkspace, projectRoot, exportTarget, doc_name, exportTypes[lsName])

                #Do the same thing for the asset file        
                if (os.path.exists(assetFile)):
                    check_permissions(abWorkspace, projectRoot, assetTarget, doc_name, exportTypes[lsName])
                
                #Flatten the image temporarily to allow TGA export
                doc.flatten

                #Check to see if the export requires alpha options
                if lsName == "a":
                    #Do our exports
                    doc.SaveAs(exportFile, optionsAlpha)
                    doc.SaveAs(assetFile, optionsAlpha)
                    print 'exporting: ', exportFile
                    print 'exporting: ', assetFile

                else:
                    #Do our exports
                    doc.SaveAs(exportFile, options)
                    doc.SaveAs(assetFile, options)
                    print 'exporting: ', exportFile
                    print 'exporting: ', assetFile
 
                #Go back to the saved history state
                doc.ActiveHistoryState = saved_state 
 
                #Hide the layer to make way for the others...
                layerSet.Visible = False

        #now that its all done, make the layer sets visible again
        for layerSet in layerSets:
            layerSet.Visible = True
        
        #Save the PSD to retain any changes and return control to the humans.
        doc.SaveAs(sourceFile, psdOptions)
        print "Reverting control to humans!"
        print "Monkey work done!\n"
 
    # If there are no layer sets present, 'gracefully' end the script.
    else:
        os.system("color 0c")
        print "OOPS! Something went wrong..."
        print "The file has no groups to export!"
        exit(0)