Skip to content

Instantly share code, notes, and snippets.

@mottosso
Last active August 24, 2016 08:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mottosso/41bf9c3ec622602f984ecf4e1d05dce1 to your computer and use it in GitHub Desktop.
Save mottosso/41bf9c3ec622602f984ecf4e1d05dce1 to your computer and use it in GitHub Desktop.
An example of publishing in Maya with Pyblish

Model publishing example

Here's an example in which two extractors operate on models of two kinds; one for animation, and one optimised for playback/preview.


Interface

Assets are assumed to be located within an objectSet with the following attributes attached:

  • bool: isAsset
  • string: family

These are of course completely arbitrary. Another common approach is to instead make the collector search for assets by convention. E.g. a model may reside within a top-level group with a "_MOD" suffix, and shaders may be all file nodes and so forth.


Usage

Copy/paste/run contents of example.py from your Script Editor, and then publish either via Python or GUI.

  • See scene.ma for an example scene with 1 asset.

Publish with GUI

# Assumes you've `pip install pyblish-lite`
import pyblish_lite
pyblish_lite.show()

Publish without GUI

# util.py is a top-level convenience layer included with pyblish-base
# for publishing via Python. Alternative are pyblish-qml or pyblish-lite,
# which offer a graphical interface over the same API (see api.py).

import pyblish.util

context = pyblish.util.publish()

for result in context.data['results']:
  for record in result['records']:
    print(record.msg)

# Details on what's inside each `result` dictionary
# - http://api.pyblish.com/pages/result.html

# Output:
# Collecting instanceA..
# Collecting instanceB..
# Extracting obj..
# Extracting proxy..

# More formatting inspiration:
# - http://learn.pyblish.com/chapters/17-report-iii.html
'''Self-contained example of publishing a model from Autodesk Maya
Plug-ins
- CollectInstances: Generic collector, assuming configuration from Maya
- ExtractObj: Export an OBJ from instances of model.animation
- ExtractProxy: Export an Alembic GPU cache from instances of model.proxy
Families
- model.animation: Geometry with topology fit for animation
- model.proxy: Low resolution geometry fit for previewing
'''
import pyblish.api
class CollectInstances(pyblish.api.ContextPlugin):
'''This docstring is meant for the user.
Some of the GUIs pick this up and draw it nicely.
It should say things about what its purpose is,
how to influence the things that it finds - such
as configurable options.
'''
# Tell Pyblish to run this during collection
order = pyblish.api.CollectorOrder
def process(self, context):
'''This docstring isn't picked up by Pyblish
It can be used for developer documentation. It could
also reside at module-level.
The developer can put multiple plug-ins within the
same module. This may influence where to document things.
'''
# Keeping the import internal to the function enables use of
# a module across multiple applications (where relevant)
from maya import cmds
# In this example, the company has decided that any asset is
# identified by storing whichever nodes it relates to within
# an objectSet with a particular attribute and value.
# This objectSet then holds attributes related to publishing
# of this asset.
for objset in cmds.ls(type='objectSet', objectsOnly=True):
if not cmds.objExists(objset + '.isAsset'):
continue
self.log.info("Collecting %s.." % objset)
instance = context.create_instance(name=objset)
# Assume families is defined in Maya
for key in cmds.listAttr(objset, userDefined=True) or list():
instance.data[key] = cmds.getAttr(objset + '.' + key)
# Include contents of set in this instance for
# subsequent processing steps, like validation.
instance[:] = cmds.sets(objset, query=True)
class ExtractObj(pyblish.api.InstancePlugin):
'''Produce an OBJ of an instance'''
order = pyblish.api.ValidatorOrder
optional = True
families = ["model.animation"]
def process(self, instance):
# Implementation left to the reader
self.log.info("Extracting obj..")
class ExtractProxy(pyblish.api.InstancePlugin):
'''Produce a GPU Proxy of an instance'''
order = pyblish.api.ValidatorOrder
optional = False
families = ['model.proxy']
def process(self, instance):
# Implementation left to the reader
self.log.info('Extracting proxy..')
# Quickly test out a series of plug-ins and their interactions
# by registering them in-memory and run them as-is.
pyblish.api.register_plugin(CollectInstances)
pyblish.api.register_plugin(ExtractObj)
pyblish.api.register_plugin(ExtractProxy)
//Maya ASCII 2016 scene
//Name: martin_example.ma
//Last modified: Tue, Aug 23, 2016 08:05:26 PM
//Codeset: 1252
requires maya "2016";
currentUnit -l centimeter -a degree -t film;
fileInfo "application" "maya";
fileInfo "product" "Maya 2016";
fileInfo "version" "2016";
fileInfo "cutIdentifier" "201511301000-979500";
fileInfo "osv" "Microsoft Windows 8 Enterprise Edition, 64-bit (Build 9200)\n";
createNode transform -s -n "persp";
rename -uid "52D8CE0F-4838-D7DE-AC7A-78B6595CEB3F";
setAttr ".v" no;
setAttr ".t" -type "double3" 2.5413039099508832 2.3053791446736431 4.5809494947684524 ;
setAttr ".r" -type "double3" -24.938352729602727 27.400000000000123 0 ;
createNode camera -s -n "perspShape" -p "persp";
rename -uid "1336AEB3-4662-946C-8442-7183C5AA657F";
setAttr -k off ".v" no;
setAttr ".fl" 34.999999999999993;
setAttr ".coi" 5.8296740112978593;
setAttr ".imn" -type "string" "persp";
setAttr ".den" -type "string" "persp_depth";
setAttr ".man" -type "string" "persp_mask";
setAttr ".hc" -type "string" "viewSet -p %camera";
createNode transform -s -n "top";
rename -uid "850049D9-4321-BB88-2C55-60A9E8273D11";
setAttr ".v" no;
setAttr ".t" -type "double3" 0 100.1 0 ;
setAttr ".r" -type "double3" -89.999999999999986 0 0 ;
createNode camera -s -n "topShape" -p "top";
rename -uid "FB4827FB-4E68-9603-CAAF-B3AA10C28FA6";
setAttr -k off ".v" no;
setAttr ".rnd" no;
setAttr ".coi" 100.1;
setAttr ".ow" 30;
setAttr ".imn" -type "string" "top";
setAttr ".den" -type "string" "top_depth";
setAttr ".man" -type "string" "top_mask";
setAttr ".hc" -type "string" "viewSet -t %camera";
setAttr ".o" yes;
createNode transform -s -n "front";
rename -uid "28C353AE-4FAE-6FCA-7137-6C9BCBBB7AC7";
setAttr ".v" no;
setAttr ".t" -type "double3" 0 0 100.1 ;
createNode camera -s -n "frontShape" -p "front";
rename -uid "9BDC1F39-4816-8D0B-950F-B69F2AC0C8FA";
setAttr -k off ".v" no;
setAttr ".rnd" no;
setAttr ".coi" 100.1;
setAttr ".ow" 30;
setAttr ".imn" -type "string" "front";
setAttr ".den" -type "string" "front_depth";
setAttr ".man" -type "string" "front_mask";
setAttr ".hc" -type "string" "viewSet -f %camera";
setAttr ".o" yes;
createNode transform -s -n "side";
rename -uid "7F572DE8-4763-322E-9717-EC963A8386A9";
setAttr ".v" no;
setAttr ".t" -type "double3" 100.1 0 0 ;
setAttr ".r" -type "double3" 0 89.999999999999986 0 ;
createNode camera -s -n "sideShape" -p "side";
rename -uid "50261B8F-409E-DBB0-2A5D-DF877BD6B6E1";
setAttr -k off ".v" no;
setAttr ".rnd" no;
setAttr ".coi" 100.1;
setAttr ".ow" 30;
setAttr ".imn" -type "string" "side";
setAttr ".den" -type "string" "side_depth";
setAttr ".man" -type "string" "side_mask";
setAttr ".hc" -type "string" "viewSet -s %camera";
setAttr ".o" yes;
createNode transform -n "pCube1";
rename -uid "24CD1333-42D4-9775-38D4-748787760323";
createNode mesh -n "pCubeShape1" -p "pCube1";
rename -uid "C2767B0A-4DB3-5907-8E07-12950E02C625";
setAttr -k off ".v";
setAttr ".vir" yes;
setAttr ".vif" yes;
setAttr ".uvst[0].uvsn" -type "string" "map1";
setAttr ".cuvs" -type "string" "map1";
setAttr ".dcc" -type "string" "Ambient+Diffuse";
setAttr ".covm[0]" 0 1 1;
setAttr ".cdvm[0]" 0 1 1;
setAttr ".vbc" no;
createNode lightLinker -s -n "lightLinker1";
rename -uid "B377EAE6-45CF-3F58-24F3-2FA225875F31";
setAttr -s 2 ".lnk";
setAttr -s 2 ".slnk";
createNode displayLayerManager -n "layerManager";
rename -uid "CA22F2E7-4BCC-882B-416B-3BBA3F47D064";
createNode displayLayer -n "defaultLayer";
rename -uid "E55803F2-475C-E15C-4A51-5CB5776A956C";
createNode renderLayerManager -n "renderLayerManager";
rename -uid "D4A6E0A1-4E95-B333-F594-B8A341E219DB";
createNode renderLayer -n "defaultRenderLayer";
rename -uid "5679E97D-4C54-58BA-C2A8-05BDDB82568E";
setAttr ".g" yes;
createNode polyCube -n "polyCube1";
rename -uid "FAD600AC-4F6A-0035-5466-E88CA250A6B8";
setAttr ".cuv" 4;
createNode objectSet -n "myModel_SET";
rename -uid "6EE3FCF0-47E2-9123-3649-1EB5E4A3C28C";
addAttr -ci true -sn "isAsset" -ln "isAsset" -min 0 -max 1 -at "bool";
addAttr -ci true -sn "family" -ln "family" -dt "string";
setAttr ".ihi" 0;
setAttr -k on ".isAsset" yes;
setAttr -k on ".family" -type "string" "model.animation";
createNode script -n "sceneConfigurationScriptNode";
rename -uid "0C0143F1-4B95-A91A-D2D9-9388B59919DE";
setAttr ".b" -type "string" "playbackOptions -min 1 -max 24 -ast 1 -aet 48 ";
setAttr ".st" 6;
createNode nodeGraphEditorInfo -n "MayaNodeEditorSavedTabsInfo";
rename -uid "9FA5D44E-4D68-C3E8-605A-F8B7A4AEF6FF";
setAttr ".pee" yes;
setAttr ".tgi[0].tn" -type "string" "Untitled_1";
setAttr ".tgi[0].vl" -type "double2" -329.76189165834455 -91.666663024160755 ;
setAttr ".tgi[0].vh" -type "double2" 197.61903976637254 234.52380020467101 ;
select -ne :time1;
setAttr ".o" 1;
setAttr ".unw" 1;
select -ne :hardwareRenderingGlobals;
setAttr ".otfna" -type "stringArray" 22 "NURBS Curves" "NURBS Surfaces" "Polygons" "Subdiv Surface" "Particles" "Particle Instance" "Fluids" "Strokes" "Image Planes" "UI" "Lights" "Cameras" "Locators" "Joints" "IK Handles" "Deformers" "Motion Trails" "Components" "Hair Systems" "Follicles" "Misc. UI" "Ornaments" ;
setAttr ".otfva" -type "Int32Array" 22 0 1 1 1 1 1
1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 ;
setAttr ".fprt" yes;
select -ne :renderPartition;
setAttr -s 2 ".st";
select -ne :renderGlobalsList1;
select -ne :defaultShaderList1;
setAttr -s 4 ".s";
select -ne :postProcessList1;
setAttr -s 2 ".p";
select -ne :defaultRenderingList1;
select -ne :initialShadingGroup;
setAttr ".ro" yes;
select -ne :initialParticleSE;
setAttr ".ro" yes;
select -ne :defaultResolution;
setAttr ".pa" 1;
select -ne :hardwareRenderGlobals;
setAttr ".ctrs" 256;
setAttr ".btrs" 512;
connectAttr "polyCube1.out" "pCubeShape1.i";
relationship "link" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message";
relationship "link" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message";
relationship "shadowLink" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message";
relationship "shadowLink" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message";
connectAttr "layerManager.dli[0]" "defaultLayer.id";
connectAttr "renderLayerManager.rlmi[0]" "defaultRenderLayer.rlid";
connectAttr "pCube1.iog" "myModel_SET.dsm" -na;
connectAttr "defaultRenderLayer.msg" ":defaultRenderingList1.r" -na;
connectAttr "pCubeShape1.iog" ":initialShadingGroup.dsm" -na;
// End of martin_example.ma
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment