mkolar / maya-look-hookup

Maya shader network transfer
MIT License
3 stars 0 forks source link

Store shader assignment data #2

Open mkolar opened 8 years ago

mkolar commented 8 years ago

Goal

Figure out how and where to store the information about shader assignments.

We're assuming that a shader network is made specifically for a given item (mesh most likely). Hence when publishing this shader we need to store the information about what item the shader should be assigned to once loaded into the scene again.

Methods

  1. Pickle the data and store it in a custom attribute directly on a shader.

    • I like this one a lot. It doesn't add almost any bulk to the scene and stays non-distruptive even if the shader is imported into a scene completely outside the pipeline. One attribute won't bother anyone and we can store quite a lot of data to it.
    • Implementation is trivial as well. This is code I just found looking through my ancient folder of bookmarks (comes from here), full code with docstrings in pickle_to_attr.py
    import cPickle
    import maya.cmds as mc
    
    def pyToAttr(objAttr, data):
    obj, attr = objAttr.split('.')
    # Add the attr if it doesn't exist:
    if not mc.objExists(objAttr):
       mc.addAttr(obj, longName=attr, dataType='string')
    # Make sure it is the correct type before modifing:
    if mc.getAttr(objAttr, type=True) != 'string':
       raise Exception("Object '%s' already has an attribute called '%s', but it isn't type 'string'"%(obj,attr))
    
    # Pickle the data and return the coresponding string value:
    stringData = cPickle.dumps(data)
    # Make sure attr is unlocked before edit:
    mc.setAttr(objAttr, edit=True, lock=False)
    # Set attr to string value:
    mc.setAttr(objAttr, stringData, type='string')
    # And lock it for safety:
    mc.setAttr(objAttr, edit=True, lock=True)
    
    def attrToPy(objAttr):+
    # Get the string representation of the pickled data.  Maya attrs return
    # unicode vals, and cPickle wants string, so we convert:
    stringAttrData = str(mc.getAttr(objAttr))
    # Un-pickle the string data:
    loadedData = cPickle.loads(stringAttrData)
    
    return loadedData
    
    # Define our node and attr name based on the selected object:
    node = mc.ls(selection=True)[0]
    objAttr = '%s.defaultAssignment'%node
    
    specialData = {node:'some_mesh_identifier'}
    print "Python data to store to Maya obj.attr:"
    print specialData
    
    # Store data to our node:
    pyToAttr(objAttr, specialData)
    
    # Later, get data back:
    storedData = attrToPy(objAttr)
    print "Restored Python Data:"
    print storedData
  2. Store it as a metadata using something like red9 Metadata nodes
  3. store it in a separate sidecar file (json for instance)
    • I'm always slightly hesitant to be adding lot's of small files, but certainly a viable option

For a start I'd advocate option 1. Once we figure out what data we want and test it with the picked data, we can determine if it's enough, or whether we need to look somewhere else.

mottosso commented 8 years ago

Why pickle? Why not a json string?

mkolar commented 8 years ago

Actually you're right. JSON would be a better option. I just though of pickle because that's what I used last time for this and I didn't give it a second thought.

Good point

mottosso commented 8 years ago

Ok, just making sure I wasn't missing anything in the code. Looked like plain-old-data to me, even though the indenting is a bit broken on the functions.

mkolar commented 8 years ago

Yeah I just dumped it here to get it started. I already have cleaned up with json now.

tokejepsen commented 8 years ago

+1 for option 11:) Storing the data as json on an attribute, is pretty much what Meadata Nodes do in red9.

Guessing you would store the UUIDs on the meshes, to make it name/namespace agnostic?

mkolar commented 8 years ago

Yep we should definitely use the UUIDs. We might want to store both the name and UUID of the mesh. Might be useful for double checks with database sometimes.

UUIDs on the meshes

I'm planning on storing mesh UUID on the shader. Nothing will be stored on the mesh, considering that when it's created modeller doesn't have the shader yet.

mottosso commented 8 years ago

Me and Roy went down this path, and it looks promising. I remember we had at least one logical problem with this though. A UUID should ideally be unique always. But what do you do when you reference a mesh? How about when you import that reference? How about when you duplicate a mesh for whatever reason? Say for example you are rigging and would like to keep the original input mesh as-is, and skin a copy of that mesh. Should the new mesh then be given a new UUID? Is it suddenly unique?

What we ended up doing was assigning a UUID once, and if there was one, we simply did not assign one. That meant duplicates maintained the UUID and all was well; but then it isn't really a UUID anymore since many meshes can have the same UUID (thus, not unique). And what if a duplicate is made, but altered to such an extent that it indeed isn't related to the orignal anymore? Say for example you're box-modeling, and you make a dozen copies of a single box during modeling.

All good things to talk about.

BigRoy commented 8 years ago

In our pipeline we also use a unique identifier, but not just a UUID. We use a unique identifier for a specific mesh (or any shape really) within an asset.

For example a character Ben with the mesh _R_shoeGEO should be identified as the exact same object anywhere it's found. E.g. when going from modeling -> rigging -> animation -> lighting through possibly multiple formats (maya files + alembics in our case). In our case this identifier is a string like: {asset}/{shape} resulting in the asset's name and the shape's unique path in the scene it was built. Ben/R_shoe_GEO. Theoretically anyone could copy/assign this identifier to "mock" it being that shape.

Using this identifier we can also "find" what shader should belong with that object. The UUID by itself doesn't provide that information by itself and you'd need a database/dictionary mapping somewhere that tells you which asset that UUID belongs with to find the shader for that asset.

The nice thing about using an identifier like this is that it's somewhat more human-readable.

Anyway, preferrably I would have this repository stay pipeline-agnostic and allow anyone to "hook" their own unique identifier into the format. Maybe by using a method like register_identifier().

The data stored could be any object that can convert to and from JSON:

{
  identifier: "Ben/R_shoe_GEO"
}

Similarly another studio could use their own identifier hook that stores other data to identify a specific object in the scene:

{
  identifier: "16fd2706-8baf-433b-82eb-8c7fada847da"
}

Does that make sense?

mkolar commented 8 years ago

In our case this identifier is a string like: {asset}/{shape}

let's go for it.

Maybe by using a method like register_identifier().

thumbs up too.