Ejemplo n.º 1
0
def test_topologyMatch(src, tgt):
    '''Tests if the topologies match between two mesh transforms
    Args:
        src (pm.nt.Transform): the source object
        tgt (pm.nt.Transform): the target object
    Returns (boolean): True if success, warning and False if not
    Usage:
        test_topologyMatch( pm.ls(sl=True)[0], pm.ls(sl=True)[1] )
    '''
    inconsistencies = {
                       128:'User Normals',
                       64:'Color Indices',
                       32:'Color Sets',
                       16:'UV Indices',
                       8:'UV sets',
                       4:'Face Desriptions',
                       2:'Edges',
                       1:'Verts'
                       }
    compare_value = pm.polyCompare([src, tgt])
    for key in reversed( sorted( inconsistencies.keys() ) ):
        if (compare_value-key) < 0:
            print "popping "+str(key)
            inconsistencies.pop(key, None)
        else: compare_value-=key
    mismatches = [inconsistencies[key] for key in sorted(inconsistencies.keys())]
    if mismatches:
        pm.warning ('Sorry, your geometry %s does not match %s,'
               'here are the things that don\'t match:'
               '\n\t%s'%(src.name(), tgt.name(), "\n\t".join(mismatches)))
        return False
    return True
Ejemplo n.º 2
0
def sendSelectedToEd():
    """
    there is the special case where there is one type of mesh in the scene
    with edited geometry but an AssetPath pointing to the unedited file
    if the user wants that object to be exported as a new asset and don't
    overwrite the existing asset, (our automation couldn't detect that, because
    all assets with same path have same geometry)
    the user would have to call sendSelectedAsNew in that case?
    Then we would make sure that the user is provided with the UI to change the
    assetPath of those objects on export.
    If the user does not choose sendAsNew, we assume he wants to overwrite any
    files on disk for sure.
    There is also the case where we don't want to export objects that are already
    in the editor, because reimporting takes time, if we didn't change the asset,
    don't ask. Also, we maybe don't want to overwrite files that already are on
    disk, because that takes time.
    We only want to create the map from the objects we have and add those objects
    that we don't have.
    So we need a third option "sendSelectedAddMissingOnly"
    We might intelligently do a check on file-date, import-date in editor
    and maybe a import-date in program for one or the other case to determine
    if a reimport or a file overwrite is necessary or not

    What is the "AssetPath" supposed to be?
    It is the file path, including the file-extension, relative to the current
    projects Art-Source folder. No absolute paths, but that is depending on the
    actual pipeline-implementation, since all functions that deal with
    file paths will be delegated to a pipeline module, and that may be replaced
    by the user.
    
    1. get selected objects
    2. for each object, get the "AssetPath" attribute
    3. if the object has no such attribute, save it in a untagged list
       if it has, save it in a tagged list entry (filepath:[objects])
    4. for each object in the tagged list, check if the file exists on disk
       fancy: check each object with same file if the gemometry is same
       - if not, tell user that objects with same file path differ and that he should
       export as new.
       - do that by adding all objects with same geometry to a list
       - then ask the user how to rename that asset
       - replace the assetPath value on all assets in that list and add
         them to the tagged unique list
    5. if it does not, export one of the associated objects as asset
    (overwrite any file found)
    6. for each object in the untagged list
       fancy: check all other objects in the untagged list if the geometry is same
       for each where it is same, insert in an association list
       (firstObject:[matchingObjects])
       fancy2: ask the user if the "unique" objects as found are ok,
               if not, he has to split objects into a new "unique" group
       for each first object in that association list
       check if the geometry is the same as in any of the tagged list uniques
       if it is, set the AssetPath attrib on all matching objects to that of the unique
       if it is not, export it as a new Asset and set the AssetPath attrib on
       all matching objects
       add all those to the tagged association list
    7. for each unique object in the tagged list
       tell the editor to import the associated file
    8. for each selected object
       tell the editor to add an actor with the AssetPath as asset
    extend to send lights and stuff like that

    this function will perform something between O(n) and O(n^2) would need to analyze this a little more
    only counting objects... vertex count may be different in scenes, and the heavier
    the vert count, again the heavier the check will be...
    """

    # 1. get selected objects (only transform nodes)
    selectedObjects = pm.selected(type="transform")
    # filter out transforms that don't have a mesh-shape node
    selectedMeshes = list()
    for obj in selectedObjects:
        meshShapes = pm.listRelatives(obj, shapes=True, type="mesh")
        if len(meshShapes) > 0:
            selectedMeshes.append(obj)
    _lg.debug("found %i selected meshes" % len(selectedMeshes))
    # TODO: maybe filter other transferable stuff like lights or so

    # 2. for each object get the "AssetPath" attribute
    untaggedList = list()
    taggedDict = {}
    for obj in selectedMeshes:
        if obj.hasAttr("AssetPath"):
            assetPathAttr = obj.attr("AssetPath")
            assetPathValue = assetPathAttr.get()
            if len(assetPathValue) > 0:
                taggedDict.setdefault(assetPathValue, []).append(obj)
            else:
                # if the ass path is empty, that is equal to the attr not being there
                untaggedList.append(obj)
        else:
            # unknown asset, we will handle those later
            untaggedList.append(obj)
    _lg.debug("found %i untagged" % len(untaggedList))
    _lg.debug("found %i tagged" % len(taggedDict))

    # 3. do the geometry check for tagged objects
    #   this assembles the taggedUniqueDict
    taggedDiscrepancyDetected = False
    taggedUniqueDict = {}
    for lis in taggedDict.values():
        # for obj in lis: #we modify lis, so iterator won't work
        while len(lis) > 0:  # use while instead
            obj = lis[0]
            taggedUniqueDict[obj] = []
            # compare this object against all others in the list.
            for otherObj in lis[1:]:
                if 0 == pm.polyCompare(obj, otherObj, vertices=True):
                    # if the geometry matches, add the other to the unique list
                    # with this object as key, and remove from the old list
                    taggedUniqueDict[obj].append(otherObj)
                    lis.remove(otherObj)
                else:
                    taggedDiscrepancyDetected = True
            lis.remove(obj)  # we are done with this object too
    _lg.debug("found %i tagged uniques" % len(taggedUniqueDict))

    # 3. do the geometry check for untagged objects
    untaggedUniquesDetected = False
    while len(untaggedList) > 0:
        obj = untaggedList[0]
        if not obj.hasAttr("AssetPath"):
            pm.addAttr(obj.name(), longName="AssetPath", dataType="string", keyable=False)
        foundUniqueForMe = False
        # compare against one of the tagged uniques
        for other in taggedUniqueDict.keys():
            # if that geometry matches, we found the unique for this obj
            if 0 == pm.polyCompare(obj, other, vertices=True):
                taggedUniqueDict[other].append(obj)
                # set "AssetPath" attr to match that of the unique
                obj.attr("AssetPath").set(other.attr("AssetPath").get())
                # we are done with this object
                untaggedList.remove(obj)
                foundUniqueForMe = True
                _lg.debug("found a unique key (%s) for %s" % (other.name(), obj.name()))
                break

        if not foundUniqueForMe:
            untaggedUniquesDetected = True
            # make this a new unique, simply take the objects name as AssetPath
            npath = obj.shortName() + "_AutoExport" + ".fbx"
            obj.attr("AssetPath").set(npath)
            taggedUniqueDict[obj] = []
            untaggedList.remove(obj)
            _lg.debug("assuming new untagged unique: " + obj.shortName())
            # we will automatically compare to all other untagged to find
            # members for our new unique in the next loop iteration
    _lg.debug("found %i uniques (with untagged)" % len(taggedUniqueDict))

    # TODO: 4. UI-stuff...
    # 4. if taggedDiscrepancy or untaggedUniques were detected,
    # list all uniques in the UI and let the user change names
    # force him to change names for taggedUniques with same AssetPath of course
    # if taggedDiscrepancyDetected:
    # for unique in taggedUniqueDict.keys():
    # it is up to the UI to do that and let the user
    # set a new assetPath on any of those unique guy's lists

    # 5. export files stuff
    for obj in taggedUniqueDict.keys():
        exportObjectAsAsset(obj.name(), obj.attr("AssetPath").get())

    # 6. tell the editor to import all the uniques
    fileList = []
    for obj in taggedUniqueDict.keys():
        fileList.append(obj.attr("AssetPath").get())
    m2u.core.getEditor().importAssetsBatch(fileList)

    # 7. tell the editor to assemble the scene
    objInfoList = []
    for obj in selectedMeshes:
        # TODO: make that a new function
        objInfo = ObjectInfo(name=obj.shortName(), typeInternal="mesh", typeCommon="mesh")
        objTransforms = m2u.maya.mayaObjectTracker.getTransformationFromObj(obj)
        objInfo.pos = objTransforms[0]
        objInfo.rot = objTransforms[1]
        objInfo.scale = objTransforms[2]
        objInfo.AssetPath = obj.attr("AssetPath").get()
        objInfoList.append(objInfo)
    m2u.core.getEditor().addActorBatch(objInfoList)
Ejemplo n.º 3
0
    def _find_uniques(self, selected_meshes):
        """Assemble the `asset_list` and detect `tagged_discrepancy`
        by checking the meshes for their `AssetPath` attribute and
        geometric parity.

        Uniques here means geometric objects that have the same geometry
        even if they are not instantiated. When normally duplicating
        objects in Maya, the copy will be a different geometry object,
        but both, original and copy, will come down to the same 'unique'
        mesh by comparing their geometry.
        """
        # For each object check the "AssetPath" attribute. Those which
        # have one are considered 'tagged'.
        untagged_list = list()
        tagged_dict = {}  # Map AssetPath strings to all objects using it
        for obj in selected_meshes:
            if obj.hasAttr("AssetPath"):
                asset_path_attr = obj.attr("AssetPath")
                asset_path_value = asset_path_attr.get()
                if len(asset_path_value) > 0:
                    tagged_dict.setdefault(asset_path_value, []).append(obj)
                else:
                    # If the asset path is empty, that is equal to the
                    # attribute not existing on the object.
                    untagged_list.append(obj)
            else:
                # Unknown asset, we will handle those later.
                untagged_list.append(obj)

        _lg.debug("Found %i untagged objects." % len(untagged_list))
        _lg.debug("Found %i tagged objects." % len(tagged_dict))

        # Do the geometry check for 'tagged' objects:
        # Assemble the `tagged_unique_dict`, which maps an object to a
        # list of all other objects that have the same geometry.
        # A 'tagged discrepancy' is when objects sharing the same
        # geometry have different "AssetPath" values.
        tagged_discrepancy_detected = False
        tagged_unique_dict = {}
        for object_list in tagged_dict.values():
            while len(object_list) > 0:
                obj = object_list[0]
                tagged_unique_dict[obj] = []
                # Compare this object against all others in the list.
                for other_obj in object_list[1:]:
                    have_same_geometry = (pm.polyCompare(obj,
                                                         other_obj,
                                                         vertices=True) == 0)
                    if have_same_geometry:
                        # If the geometry matches, add the other to the
                        # unique list with this object as key, and remove
                        # from the old list.
                        tagged_unique_dict[obj].append(other_obj)
                        object_list.remove(other_obj)
                    else:
                        # TODO: Create new unique for objects that have
                        #   same AssetPath but different geometry.
                        tagged_discrepancy_detected = True
                object_list.remove(obj)

        _lg.debug("Found %i tagged uniques." % len(tagged_unique_dict))

        # Do the geometry check for 'untagged' objects:
        # If tagged objects with the same geometry exist, add the object
        # to the respective list and use their AssetPath.
        # If there is no unique yet for the geometry, use the first
        # object with that geometry as a new unique, and use its name as
        # AssetPath.
        untagged_uniques_detected = False
        while len(untagged_list) > 0:
            obj = untagged_list[0]
            if not obj.hasAttr("AssetPath"):
                pm.addAttr(obj.name(),
                           longName="AssetPath",
                           dataType="string",
                           keyable=False)
            found_unique_for_me = False
            # Compare against one of the tagged uniques.
            for unique_obj in tagged_unique_dict.keys():
                have_same_geometry = (pm.polyCompare(obj,
                                                     unique_obj,
                                                     vertices=True) == 0)
                if have_same_geometry:
                    # If that geometry matches, we found the unique to
                    # use for this obj.
                    tagged_unique_dict[unique_obj].append(obj)
                    asset_path = unique_obj.attr("AssetPath").get()
                    obj.attr("AssetPath").set(asset_path)
                    found_unique_for_me = True
                    untagged_list.remove(obj)
                    _lg.debug(
                        "Found an existing unique key ({0}) for {1}".format(
                            unique_obj.name(), obj.name()))
                    break

            if not found_unique_for_me:
                untagged_uniques_detected = True
                # Make this a new unique. Simply take the object's name
                # as AssetPath, but remove any trailing numbers for
                # convenience.
                npath = obj.shortName()
                npath = helper.remove_number_suffix(npath)
                obj.attr("AssetPath").set(npath)
                tagged_unique_dict[obj] = []
                untagged_list.remove(obj)
                _lg.debug("Assuming new untagged unique: " + obj.shortName())
                # We will automatically compare to all other untagged
                # to find members for our new unique in the next loop
                # iteration

        _lg.debug("Found {0} uniques (including untagged).".format(
            len(tagged_unique_dict)))

        # Create a string-based asset list for the UI and further
        # operations. This is not a dict because there may be tagged
        # discrepancies (dupl keys).
        asset_list = []
        for unique_obj, instance_objects in tagged_unique_dict.items():
            path = unique_obj.attr("AssetPath").get()
            # New entry for that AssetPath with first object.
            entry = AssetListEntry(path)
            entry.append(unique_obj.shortName(), unique_obj)
            # Append all other objects that match the geo.
            for instance in instance_objects:
                entry.append(instance.shortName(), instance)
            asset_list.append(entry)

        self.asset_list = asset_list
        self.untagged_uniques_detected = untagged_uniques_detected
        self.tagged_discrepancy_detected = tagged_discrepancy_detected
Ejemplo n.º 4
0
def sendSelectedToEd():
    """
    there is the special case where there is one type of mesh in the scene
    with edited geometry but an AssetPath pointing to the unedited file
    if the user wants that object to be exported as a new asset and don't
    overwrite the existing asset, (our automation couldn't detect that, because
    all assets with same path have same geometry)
    the user would have to call sendSelectedAsNew in that case?
    Then we would make sure that the user is provided with the UI to change the
    assetPath of those objects on export.
    If the user does not choose sendAsNew, we assume he wants to overwrite any
    files on disk for sure.
    There is also the case where we don't want to export objects that are already
    in the editor, because reimporting takes time, if we didn't change the asset,
    don't ask. Also, we maybe don't want to overwrite files that already are on
    disk, because that takes time.
    We only want to create the map from the objects we have and add those objects
    that we don't have.
    So we need a third option "sendSelectedAddMissingOnly"
    We might intelligently do a check on file-date, import-date in editor
    and maybe a import-date in program for one or the other case to determine
    if a reimport or a file overwrite is necessary or not

    What is the "AssetPath" supposed to be?
    It is the file path, including the file-extension, relative to the current
    projects Art-Source folder. No absolute paths, but that is depending on the
    actual pipeline-implementation, since all functions that deal with
    file paths will be delegated to a pipeline module, and that may be replaced
    by the user.
    
    1. get selected objects
    2. for each object, get the "AssetPath" attribute
    3. if the object has no such attribute, save it in a untagged list
       if it has, save it in a tagged list entry (filepath:[objects])
    4. for each object in the tagged list, check if the file exists on disk
       fancy: check each object with same file if the gemometry is same
       - if not, tell user that objects with same file path differ and that he should
       export as new.
       - do that by adding all objects with same geometry to a list
       - then ask the user how to rename that asset
       - replace the assetPath value on all assets in that list and add
         them to the tagged unique list
    5. if it does not, export one of the associated objects as asset
    (overwrite any file found)
    6. for each object in the untagged list
       fancy: check all other objects in the untagged list if the geometry is same
       for each where it is same, insert in an association list
       (firstObject:[matchingObjects])
       fancy2: ask the user if the "unique" objects as found are ok,
               if not, he has to split objects into a new "unique" group
       for each first object in that association list
       check if the geometry is the same as in any of the tagged list uniques
       if it is, set the AssetPath attrib on all matching objects to that of the unique
       if it is not, export it as a new Asset and set the AssetPath attrib on
       all matching objects
       add all those to the tagged association list
    7. for each unique object in the tagged list
       tell the editor to import the associated file
    8. for each selected object
       tell the editor to add an actor with the AssetPath as asset
    extend to send lights and stuff like that

    this function will perform something between O(n) and O(n^2) would need to analyze this a little more
    only counting objects... vertex count may be different in scenes, and the heavier
    the vert count, again the heavier the check will be...
    """

    #1. get selected objects (only transform nodes)
    selectedObjects = pm.selected(type="transform")
    # filter out transforms that don't have a mesh-shape node
    selectedMeshes = list()
    for obj in selectedObjects:
        meshShapes = pm.listRelatives(obj, shapes=True, type="mesh")
        if len(meshShapes)>0:
            selectedMeshes.append(obj)
    _lg.debug("found %i selected meshes" % len(selectedMeshes))
    # TODO: maybe filter other transferable stuff like lights or so
    
    #2. for each object get the "AssetPath" attribute
    untaggedList = list()
    taggedDict = {}
    for obj in selectedMeshes:
        if obj.hasAttr("AssetPath"):
            assetPathAttr = obj.attr("AssetPath")
            assetPathValue = assetPathAttr.get()
            if len(assetPathValue)>0:
                taggedDict.setdefault(assetPathValue,[]).append(obj)
            else:
                # if the ass path is empty, that is equal to the attr not being there
                untaggedList.append(obj)
        else:
            # unknown asset, we will handle those later
            untaggedList.append(obj)
    _lg.debug("found %i untagged" % len(untaggedList))
    _lg.debug("found %i tagged" % len(taggedDict))
    
    #3. do the geometry check for tagged objects
    #   this assembles the taggedUniqueDict
    taggedDiscrepancyDetected = False
    taggedUniqueDict = {}
    for lis in taggedDict.values():
        #for obj in lis: #we modify lis, so iterator won't work
        while len(lis)>0: # use while instead
            obj = lis[0]
            taggedUniqueDict[obj]=[]
            # compare this object against all others in the list.
            for otherObj in lis[1:]:
                if 0 == pm.polyCompare(obj, otherObj, vertices=True):
                    # if the geometry matches, add the other to the unique list
                    # with this object as key, and remove from the old list
                    taggedUniqueDict[obj].append(otherObj)
                    lis.remove(otherObj)
                else:
                    taggedDiscrepancyDetected = True
            lis.remove(obj) # we are done with this object too
    _lg.debug("found %i tagged uniques" % len(taggedUniqueDict))
    
    #3. do the geometry check for untagged objects
    untaggedUniquesDetected = False
    while len(untaggedList)>0:
        obj = untaggedList[0]
        if not obj.hasAttr("AssetPath"):
            pm.addAttr(obj.name(), longName="AssetPath", dataType="string",
                       keyable=False)
        foundUniqueForMe = False
        # compare against one of the tagged uniques
        for other in taggedUniqueDict.keys():
            # if that geometry matches, we found the unique for this obj
            if 0 == pm.polyCompare(obj, other, vertices=True):
                taggedUniqueDict[other].append(obj)
                # set "AssetPath" attr to match that of the unique
                obj.attr("AssetPath").set(other.attr("AssetPath").get())
                # we are done with this object
                untaggedList.remove(obj)
                foundUniqueForMe = True
                _lg.debug("found a unique key (%s) for %s" %(other.name(), obj.name()))
                break
        
        if not foundUniqueForMe:
            untaggedUniquesDetected = True
            # make this a new unique, simply take the objects name as AssetPath
            npath = obj.shortName() + "_AutoExport" + ".fbx"
            obj.attr("AssetPath").set(npath)
            taggedUniqueDict[obj]=[]
            untaggedList.remove(obj)
            _lg.debug("assuming new untagged unique: "+obj.shortName())
            # we will automatically compare to all other untagged to find
            # members for our new unique in the next loop iteration
    _lg.debug("found %i uniques (with untagged)" % len(taggedUniqueDict))
    
    # TODO: 4. UI-stuff...
    #4. if taggedDiscrepancy or untaggedUniques were detected,
    # list all uniques in the UI and let the user change names
    # force him to change names for taggedUniques with same AssetPath of course
    # if taggedDiscrepancyDetected:
        #for unique in taggedUniqueDict.keys():
            # it is up to the UI to do that and let the user
            # set a new assetPath on any of those unique guy's lists
    
    #5. export files stuff
    for obj in taggedUniqueDict.keys():
        exportObjectAsAsset(obj.name(), obj.attr("AssetPath").get())
        
    #6. tell the editor to import all the uniques
    fileList = []
    for obj in taggedUniqueDict.keys():
        fileList.append(obj.attr("AssetPath").get())
    m2u.core.getEditor().importAssetsBatch(fileList)
        
    #7. tell the editor to assemble the scene
    objInfoList = []
    for obj in selectedMeshes:
        # TODO: make that a new function
        objInfo = ObjectInfo(name = obj.shortName(), typeInternal = "mesh",
                             typeCommon = "mesh")
        objTransforms = m2u.maya.mayaObjectTracker.getTransformationFromObj(obj)
        objInfo.pos = objTransforms[0]
        objInfo.rot = objTransforms[1]
        objInfo.scale = objTransforms[2]
        objInfo.AssetPath = obj.attr("AssetPath").get()
        objInfoList.append(objInfo)
    m2u.core.getEditor().addActorBatch(objInfoList)