Example #1
0
def copySkinLayers(srcMeshName, destMeshName, layers, influenceAssociation, surfaceAssociation, sampleSpace, normalize, uv=None):
    '''
    layers [list] - ids of layers to be copied
    if layers is [], all layers will be copied
    '''
    srcMll = MllInterface()
    destMll = MllInterface()
    
    srcMll.setCurrentMesh(srcMeshName)
    destMll.setCurrentMesh(destMeshName)
    
    # check that selected objects are valid
    if False in (srcMll.getLayersAvailable(), destMll.getLayersAvailable()):
        mc.error("Skinning layers must be initialized on both source and destination meshes")
    
    if layers == []:
        layers = [layerId for layerId, _ in srcMll.listLayers()]
        layers.reverse()
        
    for eachLayer in layers:
        copySkinLayerById(srcMll, destMll, eachLayer, influenceAssociation, surfaceAssociation, sampleSpace, normalize, uv)
Example #2
0
class LayerData(object):
    '''
    Intermediate data object between ngSkinTools core and importers/exporters,
    representing all layers info in one skin cluster.
    
    .. py:attribute:: layers
        
        a list of :py:class:`Layer` objects.
        
    .. py:attribute:: influences
        
        a list of :py:class:`InfluenceInfo` objects. Provides information about influences
        that were found on exported skin data, and used for influence matching when importing.

    '''
    def __init__(self):
        self.layers = []
        self.mll = MllInterface()

        self.meshInfo = MeshInfo()

        self.influences = []

        # a map [sourceInfluenceName] -> [destinationInfluenceName]
        self.mirrorInfluenceAssociationOverrides = None

        self.skinClusterFn = None

    def addMirrorInfluenceAssociationOverride(self,
                                              sourceInfluence,
                                              destinationInfluence=None,
                                              selfReference=False,
                                              bidirectional=True):
        '''
        Adds mirror influence association override, similar to UI of "Add influences association".
        Self reference creates a source<->source association, bidirectional means that destination->source 
        link is added as well
        '''

        if self.mirrorInfluenceAssociationOverrides is None:
            self.mirrorInfluenceAssociationOverrides = {}

        if selfReference:
            self.mirrorInfluenceAssociationOverrides[
                sourceInfluence] = sourceInfluence
            return

        if destinationInfluence is None:
            raise MessageException("destination influence must be specified")

        self.mirrorInfluenceAssociationOverrides[
            sourceInfluence] = destinationInfluence

        if bidirectional:
            self.mirrorInfluenceAssociationOverrides[
                destinationInfluence] = sourceInfluence

    def addLayer(self, layer):
        '''
        register new layer into this data object
        
        :param Layer layer: layer object to add.
        '''
        assert isinstance(layer, Layer)
        self.layers.append(layer)

    @staticmethod
    def getFullNodePath(nodeName):
        result = cmds.ls(nodeName, l=True)
        if result is None or len(result) == 0:
            raise MessageException("node %s was not found" % nodeName)

        return result[0]

    def loadInfluenceInfo(self):
        self.influences = self.mll.listInfluenceInfo()

    def loadFrom(self, mesh):
        '''
        loads data from actual skin cluster and prepares it for exporting.
        supply skin cluster or skinned mesh as an argument
        '''

        self.mll.setCurrentMesh(mesh)

        meshExporter = MeshDataExporter()
        self.meshInfo = MeshInfo()
        if mesh != MllInterface.TARGET_REFERENCE_MESH:
            mesh, skinCluster = self.mll.getTargetInfo()
            meshExporter.setTransformMatrixFromNode(mesh)
            meshExporter.useSkinClusterInputMesh(skinCluster)
            self.meshInfo.verts, self.meshInfo.triangles = meshExporter.export(
            )
        else:
            self.meshInfo.verts = self.mll.getReferenceMeshVerts()
            self.meshInfo.triangles = self.mll.getReferenceMeshTriangles()

        self.loadInfluenceInfo()

        # map LayerId->layerIndex
        layerIndexById = {}
        for index, (layerID, layerName,
                    parentId) in enumerate(self.mll.listLayers()):
            layerIndexById[layerID] = index
            self.mirrorInfluenceAssociationOverrides = self.mll.getManualMirrorInfluences(
            )
            if len(self.mirrorInfluenceAssociationOverrides) == 0:
                self.mirrorInfluenceAssociationOverrides = None

            layer = Layer()
            layer.name = layerName
            self.addLayer(layer)

            layer.opacity = self.mll.getLayerOpacity(layerID)
            layer.enabled = self.mll.isLayerEnabled(layerID)

            layer.mask = self.mll.getLayerMask(layerID)
            layer.dqWeights = self.mll.getDualQuaternionWeights(layerID)

            # transform parent ID to local index in the model
            if parentId is not None:
                layer.parent = layerIndexById[parentId]

            for inflName, logicalIndex in self.mll.listLayerInfluences(
                    layerID, activeInfluences=True):
                if inflName == '':
                    inflName = None
                influence = Influence()
                if inflName is not None:
                    influence.influenceName = self.getFullNodePath(inflName)
                influence.logicalIndex = logicalIndex
                layer.addInfluence(influence)

                influence.weights = self.mll.getInfluenceWeights(
                    layerID, logicalIndex)

    def __validate(self):

        numVerts = self.mll.getVertCount()

        def validateVertCount(count, message):
            if count != numVerts:
                raise Exception(message)

        for layer in self.layers:
            if layer.mask is not None and len(layer.mask) != 0:
                validateVertCount(
                    len(layer.mask),
                    "Invalid vertex count for mask in layer '%s': expected size is %d"
                    % (layer.name, numVerts))

            for influence in layer.influences:
                validateVertCount(
                    len(influence.weights),
                    "Invalid weights count for influence '%s' in layer '%s': expected size is %d"
                    % (influence.influenceName, layer.name, numVerts))

                if self.skinClusterFn:
                    influence.logicalIndex = self.skinClusterFn.getLogicalInfluenceIndex(
                        influence.influenceName)

    @Utils.undoable
    def saveTo(self, mesh):
        '''
        saveTo(self,mesh)
        
        saves data to actual skin cluster
        '''

        # set target to whatever was provided
        self.mll.setCurrentMesh(mesh)

        if mesh == MllInterface.TARGET_REFERENCE_MESH:
            self.mll.setWeightsReferenceMesh(self.meshInfo.verts,
                                             self.meshInfo.triangles)

        if not self.mll.getLayersAvailable():
            self.mll.initLayers()

        if not self.mll.getLayersAvailable():
            raise Exception("could not initialize layers")

        # is skin cluster available?
        if mesh != MllInterface.TARGET_REFERENCE_MESH:
            mesh, self.skinCluster = self.mll.getTargetInfo()
            self.skinClusterFn = SkinClusterFn()
            self.skinClusterFn.setSkinCluster(self.skinCluster)

        self.__validate()

        # set target to actual mesh
        self.mll.setCurrentMesh(mesh)

        with self.mll.batchUpdateContext():
            if self.mirrorInfluenceAssociationOverrides:
                self.mll.setManualMirrorInfluences(
                    self.mirrorInfluenceAssociationOverrides)

            # IDS of created layers, in reverse order
            layerIds = []
            for layer in reversed(self.layers):
                layerId = self.mll.createLayer(name=layer.name,
                                               forceEmpty=True)
                layerIds.append(layerId)

                self.mll.setCurrentLayer(layerId)
                if layerId is None:
                    raise Exception(
                        "import failed: could not create layer '%s'" %
                        (layer.name))

                self.mll.setLayerOpacity(layerId, layer.opacity)
                self.mll.setLayerEnabled(layerId, layer.enabled)
                self.mll.setLayerMask(layerId, layer.mask)
                self.mll.setDualQuaternionWeights(layerId, layer.dqWeights)

                self.mll.setLayerWeightsBufferSize(layerId,
                                                   len(layer.influences))
                for influence in layer.influences:
                    # because layer was just created and will belong to same undo chunk, disabling undo
                    # for setting weights bit
                    self.mll.setInfluenceWeights(layerId,
                                                 influence.logicalIndex,
                                                 influence.weights,
                                                 undoEnabled=False)

            # layer.parent reference index is in normal order, therefore need to reverse it again
            layerIds = list(reversed(layerIds))

            for index, layer in enumerate(self.layers):
                if layer.parent is not None:
                    self.mll.setLayerParent(layerIds[index],
                                            layerIds[layer.parent])
                    # reparenting will move it to the end of the list; move it to the bottom instead
                    self.mll.setLayerIndex(layerIds[index], 0)

    def __repr__(self):
        return "[LayerDataModel(%r)]" % self.layers

    def getAllInfluences(self):
        '''
        a convenience method to retrieve a list of names of all influences used in this layer data object
        '''

        result = set()

        for layer in self.layers:
            for influence in layer.influences:
                result.add(influence.influenceName)

        return tuple(result)
Example #3
0
class LayerDataModel:
    log = getLogger("layerDataModel")

    # holds instance of singleton object
    __instance = None

    @staticmethod
    def getInstance():
        '''
        returns singleton instance of LayerDataModel
        
        :rtype: LayerDataModel
        '''
        return LayerDataModel.__instance

    @classmethod
    def bindAll(cls):
        cls.__instance = LayerDataModel()

        MayaEvents.undoRedoExecuted.addHandler(
            cls.__instance.updateLayerAvailability)
        MayaEvents.nodeSelectionChanged.addHandler(
            cls.__instance.updateLayerAvailability)

        cls.__instance.updateLayerAvailability()

    def __init__(self):
        self.layerDataAvailable = None
        self.mll = MllInterface()
        self.clipboard = WeightsClipboard(self.mll)

    def setLayerListsUI(self, ui):
        self.layerListsUI = ui

    def getLayerListsUI(self):
        '''
        :rtype: LayerListsUI
        '''
        from ngSkinTools.ui import mainwindow
        mainWindow = mainwindow.MainWindow.getInstance()
        if mainWindow is None:
            return None
        return mainWindow.getLayersUI()

    def getSelectedLayer(self):
        listsUi = self.getLayerListsUI()
        if listsUi is None:
            return None
        return listsUi.getLayersList().getSelectedID()

    def getSelectedLayers(self):
        listsUi = self.getLayerListsUI()
        if listsUi is None:
            return []
        return listsUi.getSelectedLayers()

    def getSelectedInfluenceIds(self):
        listsUi = self.getLayerListsUI()
        if listsUi is None:
            return []
        return listsUi.getSelectedInfluenceIds()

    def updateLayerAvailability(self):
        '''
        checks if availability of skin layers changed with the 
        current scene selection 
        '''
        self.log.info("updating layer availability")

        oldValue = self.layerDataAvailable
        self.layerDataAvailable = self.mll.getLayersAvailable()
        if self.layerDataAvailable != oldValue:
            LayerEvents.layerAvailabilityChanged.emit()

    @Utils.undoable
    def addLayer(self, name):
        def guessParent():
            currentLayer = self.mll.getCurrentLayer()
            if currentLayer is None:
                return None
            # guess layer's new parent
            parentsByLayerId = dict([
                (layerId, parentId)
                for layerId, _, parentId in self.mll.listLayers()
                if currentLayer in (layerId, parentId)
            ])

            # current layer is a parent?
            if currentLayer in parentsByLayerId.values():
                return currentLayer

            # current layer has parent
            if currentLayer in parentsByLayerId.keys():
                return parentsByLayerId[currentLayer]

        layerId = self.mll.createLayer(name)
        self.mll.setLayerParent(layerId, guessParent())

        if layerId is None:
            return None

        LayerEvents.layerListModified.emit()

        self.setCurrentLayer(layerId)
        return layerId

    def removeLayer(self, layerId):
        self.mll.deleteLayer(layerId)
        LayerEvents.layerListModified.emit()
        LayerEvents.currentLayerChanged.emit()

    def setCurrentLayer(self, layerId):
        self.mll.setCurrentLayer(layerId)
        LayerEvents.currentLayerChanged.emit()

    def getCurrentLayer(self):
        return self.mll.getCurrentLayer()

    def attachLayerData(self):
        self.mll.initLayers()
        with self.mll.batchUpdateContext():
            self.addLayer('Base Weights')

        self.updateLayerAvailability()
        selectionState.selectionInfo.dropCache()

    def cleanCustomNodes(self):
        '''
        removes all custom nodes from current scene
        '''

        # just in case we were in the middle of painting
        cmds.setToolTo('selectSuperContext')

        LayerUtils.deleteCustomNodes()

        # notify the rest of the world
        self.updateLayerAvailability()
        selectionState.selectionInfo.dropCache()

    def getLayerName(self, layerId):
        return mel.eval('ngSkinLayer -id {0} -q -name'.format(int(layerId)))

    def setLayerName(self, layerId, name):
        self.mll.setLayerName(layerId, name)
        LayerEvents.nameChanged.emit()

    def getLayerOpacity(self, layerId):
        return mel.eval('ngSkinLayer -id {0} -q -opacity'.format(layerId))

    def getLayerEnabled(self, layerId):
        return mel.eval('ngSkinLayer -id {0} -q -enabled'.format(layerId))

    def setLayerEnabled(self, layerId, enabled):
        cmds.ngSkinLayer(e=True, id=layerId, enabled=1 if enabled else 0)

    def toggleLayerEnabled(self, layerId):
        self.setLayerEnabled(layerId, not self.getLayerEnabled(layerId))

    def getLayersCandidateFromSelection(self):
        '''
        for given selection, returns mesh and skin cluster node names where skinLayer data
        is (or can be) attached. 
        '''
        return self.mll.getTargetInfo()

    def getLayersAvailable(self):
        self.updateLayerAvailability()
        return self.layerDataAvailable

    def isDqMode(self):
        '''
        returns True if current skin cluster is operating in dual quaternion mode
        '''
        target = self.mll.getTargetInfo()
        if not target:
            return False
        skinCluster = target[1]
        return cmds.skinCluster(skinCluster, q=True, skinMethod=True) == 2
Example #4
0
 def ensureTargetMeshLayers(self):
     targetMll = MllInterface(mesh=self.targetMesh)
     if not targetMll.getLayersAvailable():
         targetMll.initLayers()
Example #5
0
class LayerDataModel:
    log = LoggerFactory.getLogger("layerDataModel")

    class MirrorCacheStatus:
        def __init__(self):
            self.isValid = None
            self.message = None
            self.mirrorAxis = None
    
    
    # holds instance of singleton object
    __instance = None
    
    @staticmethod
    def getInstance():
        '''
        returns singleton instance of LayerDataModel
        
        :rtype: LayerDataModel
        '''
        if LayerDataModel.__instance is None:
            LayerDataModel.__instance = LayerDataModel()
        
        return LayerDataModel.__instance
    
    @staticmethod
    def reset():
        LayerDataModel.__instance = None
    
    def __init__(self):
        self.layerListsUI = None # :type: LayerListsUI
        self.layerDataAvailable = None
        self.mirrorCache = self.MirrorCacheStatus()
        self.mll = MllInterface()
        self.clipboard = WeightsClipboard(self.mll)
        
        MayaEvents.undoRedoExecuted.addHandler(self.updateLayerAvailability)
        MayaEvents.nodeSelectionChanged.addHandler(self.updateLayerAvailability)
        
        self.updateLayerAvailability()
        
    def setLayerListsUI(self,ui):
        self.layerListsUI = ui
        
    def getSelectedLayer(self):
        if self.layerListsUI is None:
            return None
        return self.layerListsUI.getLayersList().getSelectedID()
    
    def updateLayerAvailability(self):
        '''
        checks if availability of skin layers changed with the 
        current scene selection 
        '''
        self.log.info("updating layer availability")
        

        oldValue = self.layerDataAvailable
        self.layerDataAvailable = self.mll.getLayersAvailable()
        if self.layerDataAvailable!=oldValue:
            LayerEvents.layerAvailabilityChanged.emit()
        self.updateMirrorCacheStatus()
        
    def updateMirrorCacheStatus(self):
        def setStatus(newStatus,message,axis=None):
            change = newStatus != self.mirrorCache.isValid or self.mirrorCache.message != message or self.mirrorCache.mirrorAxis != axis
              
            self.mirrorCache.message = message
            self.mirrorCache.isValid = newStatus
            self.mirrorCache.mirrorAxis = axis

            if change:
                self.log.info("mirror cache status changed to %s." % self.mirrorCache.message)
                LayerEvents.mirrorCacheStatusChanged.emit()        

        self.log.info("updating mirror cache status")
        if not self.layerDataAvailable:
            setStatus(False,"Layer Data is not available")
            return
        
        try:
            cacheInfo = cmds.ngSkinLayer(q=True,mirrorCacheInfo=True)
            if cacheInfo[0]=='ok':
                setStatus(True,'Mirror Data Initialized',cmds.ngSkinLayer(q=True,mirrorAxis=True))
            else:
                setStatus(False,cacheInfo[1])
        except :
            setStatus(False,'Cache check failed')
            #log.error("error: "+str(err))
        
            
    def addLayer(self,name):
        layerId = self.mll.createLayer(name)
        
        if layerId is None:
            return
        LayerEvents.layerListModified.emit()
        
        self.setCurrentLayer(layerId)
        
    def removeLayer(self,layerId):
        self.mll.deleteLayer(layerId)
        LayerEvents.layerListModified.emit()
        LayerEvents.currentLayerChanged.emit()
        
        
    def setCurrentLayer(self,layerId):
        self.mll.setCurrentLayer(layerId)
        LayerEvents.currentLayerChanged.emit()
        
    def getCurrentLayer(self):
        return self.mll.getCurrentLayer()
        
    def attachLayerData(self):
        self.mll.initLayers()
        self.addLayer('Base Weights')

        
        self.updateLayerAvailability() 

        
        
    def cleanCustomNodes(self):
        '''
        removes all custom nodes from current scene
        '''
        LayerUtils.deleteCustomNodes()
        
        self.updateLayerAvailability()
        
    def getLayerName(self,layerId):
        return mel.eval('ngSkinLayer -id %d -q -name' % layerId)       
    
    def setLayerName(self,layerId,name):
        self.mll.setLayerName(layerId,name)
        LayerEvents.nameChanged.emit()   

    def getLayerOpacity(self,layerId):
        return mel.eval('ngSkinLayer -id %d -q -opacity' % layerId)

    def getLayerEnabled(self,layerId):
        return mel.eval('ngSkinLayer -id %d -q -enabled' % layerId)
    
    def setLayerEnabled(self,layerId,enabled):
        cmds.ngSkinLayer(e=True,id=layerId,enabled=1 if enabled else 0)
        
    def toggleLayerEnabled(self,layerId):
        self.setLayerEnabled(layerId, not self.getLayerEnabled(layerId))
            
    def getLayersCandidateFromSelection(self):
        '''
        for given selection, returns mesh and skin cluster node names where skinLayer data
        is (or can be) attached. 
        '''
        return self.mll.getTargetInfo()

    
    def getLayersAvailable(self):
        self.updateLayerAvailability()
        return self.layerDataAvailable
    
    def isDqMode(self):
        '''
        returns True if current skin cluster is operating in dual quaternion mode
        '''
        target = self.mll.getTargetInfo()
        if not target:
            return False   
        skinCluster = target[1]     
        return cmds.skinCluster(skinCluster,q=True,skinMethod=True)==2    
Example #6
0
class LayerData(object):
    '''
    Intermediate data object between ngSkinTools core and importers/exporters,
    representing all layers info in one skin cluster.
    
    .. py:attribute:: layers
        
        a list of :py:class:`Layer` objects.
        
    .. py:attribute:: influences
        
        a list of :py:class:`InfluenceInfo` objects. Provides information about influences
        that were found on exported skin data, and used for influence matching when importing.

    '''
    
    def __init__(self):
        self.layers = []
        self.mll = MllInterface()

        self.meshInfo = MeshInfo()
        
        self.influences = []
        
        # a map [sourceInfluenceName] -> [destinationInfluenceName]
        self.mirrorInfluenceAssociationOverrides = None
        
        self.skinClusterFn = None
        
    def addMirrorInfluenceAssociationOverride(self,sourceInfluence,destinationInfluence=None,selfReference=False,bidirectional=True):
        '''
        Adds mirror influence association override, similar to UI of "Add influences association".
        Self reference creates a source<->source association, bidirectional means that destination->source 
        link is added as well
        '''
        
        if self.mirrorInfluenceAssociationOverrides is None:
            self.mirrorInfluenceAssociationOverrides = {}
        
        if selfReference:
            self.mirrorInfluenceAssociationOverrides[sourceInfluence] = sourceInfluence
            return
        
        if destinationInfluence is None:
            raise MessageException("destination influence must be specified")
        
        self.mirrorInfluenceAssociationOverrides[sourceInfluence] = destinationInfluence
        
        if bidirectional:
            self.mirrorInfluenceAssociationOverrides[destinationInfluence] = sourceInfluence 
        

    def addLayer(self, layer):
        '''
        register new layer into this data object
        
        :param Layer layer: layer object to add.
        '''
        assert isinstance(layer, Layer)
        self.layers.append(layer)
       
    @staticmethod 
    def getFullNodePath(nodeName):
        result = cmds.ls(nodeName,l=True)
        if result is None or len(result)==0:
            raise MessageException("node %s was not found" % nodeName)
        
        return result[0]
    
    def loadInfluenceInfo(self):    
        self.influences = self.mll.listInfluenceInfo()
    
    def loadFrom(self, mesh):
        '''
        loads data from actual skin cluster and prepares it for exporting.
        supply skin cluster or skinned mesh as an argument
        '''
        
        self.mll.setCurrentMesh(mesh)
        
        meshExporter = MeshDataExporter()
        self.meshInfo = MeshInfo()
        if mesh!=MllInterface.TARGET_REFERENCE_MESH:
            mesh,skinCluster = self.mll.getTargetInfo()
            meshExporter.setTransformMatrixFromNode(mesh)
            meshExporter.useSkinClusterInputMesh(skinCluster)
            self.meshInfo.verts,self.meshInfo.triangles = meshExporter.export()
        else:
            self.meshInfo.verts = self.mll.getReferenceMeshVerts()
            self.meshInfo.triangles = self.mll.getReferenceMeshTriangles()

        self.loadInfluenceInfo()
        
        for layerID, layerName in self.mll.listLayers():
            self.mirrorInfluenceAssociationOverrides = self.mll.getManualMirrorInfluences()
            if len(self.mirrorInfluenceAssociationOverrides)==0:
                self.mirrorInfluenceAssociationOverrides = None
            
            layer = Layer()
            layer.name = layerName
            self.addLayer(layer)
            
            
            layer.opacity = self.mll.getLayerOpacity(layerID)
            layer.enabled = self.mll.isLayerEnabled(layerID)
            
            layer.mask = self.mll.getLayerMask(layerID)
            layer.dqWeights = self.mll.getDualQuaternionWeights(layerID)
            
            for inflName, logicalIndex in self.mll.listLayerInfluences(layerID,activeInfluences=True):
                if inflName=='':
                    inflName = None
                influence = Influence()
                if inflName is not None:
                    influence.influenceName = self.getFullNodePath(inflName)
                influence.logicalIndex = logicalIndex
                layer.addInfluence(influence)
                
                influence.weights = self.mll.getInfluenceWeights(layerID, logicalIndex)
                
    def __validate(self):
        
        numVerts = self.mll.getVertCount()
        
        def validateVertCount(count,message):
                if count!=numVerts:
                    raise Exception(message) 
        
        for layer in self.layers:
            maskLen = len(layer.mask)
            if maskLen != 0:
                validateVertCount(maskLen, "Invalid vertex count for mask in layer '%s': expected size is %d" % (layer.name, numVerts))
            
            for influence in layer.influences:
                validateVertCount(len(influence.weights), "Invalid weights count for influence '%s' in layer '%s': expected size is %d" % (influence.influenceName, layer.name, numVerts))
                
                if self.skinClusterFn:
                    influence.logicalIndex = self.skinClusterFn.getLogicalInfluenceIndex(influence.influenceName)
                
        
    @Utils.undoable        
    def saveTo(self, mesh):
        '''
        saveTo(self,mesh)
        
        saves data to actual skin cluster
        '''
        
        # set target to whatever was provided
        self.mll.setCurrentMesh(mesh)

        if mesh==MllInterface.TARGET_REFERENCE_MESH:
            self.mll.setWeightsReferenceMesh(self.meshInfo.verts, self.meshInfo.triangles)
            
        
        if not self.mll.getLayersAvailable():
            self.mll.initLayers()
            
        if not self.mll.getLayersAvailable():
            raise Exception("could not initialize layers")
        

        # is skin cluster available?
        if mesh!=MllInterface.TARGET_REFERENCE_MESH:
            mesh, self.skinCluster = self.mll.getTargetInfo()
            self.skinClusterFn = SkinClusterFn()
            self.skinClusterFn.setSkinCluster(self.skinCluster)
        
        self.__validate()
        
        # set target to actual mesh
        self.mll.setCurrentMesh(mesh)
            
        with self.mll.batchUpdateContext():
            if self.mirrorInfluenceAssociationOverrides:
                self.mll.setManualMirrorInfluences(self.mirrorInfluenceAssociationOverrides)
            
            for layer in reversed(self.layers):
                layerId = self.mll.createLayer(name=layer.name, forceEmpty=True)
                self.mll.setCurrentLayer(layerId)
                if layerId is None:
                    raise Exception("import failed: could not create layer '%s'" % (layer.name))
                
                self.mll.setLayerOpacity(layerId, layer.opacity)
                self.mll.setLayerEnabled(layerId, layer.enabled)
                self.mll.setLayerMask(layerId, layer.mask)
                self.mll.setDualQuaternionWeights(layerId, layer.dqWeights)
                
                for influence in layer.influences:
                    self.mll.setInfluenceWeights(layerId, influence.logicalIndex, influence.weights)
        
                
    def __repr__(self):
        return "[LayerDataModel(%r)]" % self.layers
    
    def getAllInfluences(self):
        '''
        a convenience method to retrieve a list of names of all influences used in this layer data object
        '''
        
        result = set()
        
        for layer in self.layers:
            for influence in layer.influences:
                result.add(influence.influenceName)
                
        return tuple(result)
Example #7
0
class LayerDataModel:
    log = LoggerFactory.getLogger("layerDataModel")

    class MirrorCacheStatus:
        def __init__(self):
            self.isValid = None
            self.message = None
            self.mirrorAxis = None

    # holds instance of singleton object
    __instance = None

    @staticmethod
    def getInstance():
        '''
        returns singleton instance of LayerDataModel
        '''
        if LayerDataModel.__instance is None:
            LayerDataModel.__instance = LayerDataModel()

        return LayerDataModel.__instance

    @staticmethod
    def reset():
        LayerDataModel.__instance = None

    def __init__(self):
        self.layerListsUI = None
        self.layerDataAvailable = None
        self.mirrorCache = self.MirrorCacheStatus()
        self.mll = MllInterface()
        self.clipboard = WeightsClipboard(self.mll)

        MayaEvents.undoRedoExecuted.addHandler(self.updateLayerAvailability)
        MayaEvents.nodeSelectionChanged.addHandler(
            self.updateLayerAvailability)

        self.updateLayerAvailability()

    def setLayerListsUI(self, ui):
        self.layerListsUI = ui

    def getSelectedLayer(self):
        if self.layerListsUI is None:
            return None
        return self.layerListsUI.getLayersList().getSelectedID()

    def updateLayerAvailability(self):
        '''
        updates interface visibility depending on availability of layer data 
        '''
        self.log.info("updating layer availability")

        oldValue = self.layerDataAvailable
        self.layerDataAvailable = self.mll.getLayersAvailable()
        if self.layerDataAvailable != oldValue:
            LayerEvents.layerAvailabilityChanged.emit()

        self.updateMirrorCacheStatus()

    def updateMirrorCacheStatus(self):
        def setStatus(newStatus, message, axis=None):
            change = newStatus != self.mirrorCache.isValid or self.mirrorCache.message != message or self.mirrorCache.mirrorAxis != axis

            self.mirrorCache.message = message
            self.mirrorCache.isValid = newStatus
            self.mirrorCache.mirrorAxis = axis
            if change:
                self.log.info("mirror cache status changed to %s." %
                              self.mirrorCache.message)
                LayerEvents.mirrorCacheStatusChanged.emit()

        self.log.info("updating mirror cache status")
        if not self.layerDataAvailable:
            setStatus(False, "Layer Data is not available")
            return

        try:
            cacheInfo = cmds.ngSkinLayer(q=True, mirrorCacheInfo=True)
            if cacheInfo[0] == 'ok':
                setStatus(True, 'Mirror Data Initialized',
                          cmds.ngSkinLayer(q=True, mirrorAxis=True))
            else:
                setStatus(False, cacheInfo[1])
        except:
            setStatus(False, 'Cache check failed')
            #log.error("error: "+str(err))

    def addLayer(self, name):
        id = self.mll.createLayer(name)

        if id is None:
            return
        LayerEvents.layerListModified.emit()

        self.setCurrentLayer(id)

    def removeLayer(self, id):
        cmds.ngSkinLayer(rm=True, id=id)
        LayerEvents.layerListModified.emit()
        LayerEvents.currentLayerChanged.emit()

    def setCurrentLayer(self, id):

        cmds.ngSkinLayer(cl=id)
        LayerEvents.currentLayerChanged.emit()

    def getCurrentLayer(self):
        return self.mll.getCurrentLayer()
        return cmds.ngSkinLayer(q=True, cl=True)

    def attachLayerData(self):
        self.mll.initLayers()
        self.addLayer('Base Weights')

        self.updateLayerAvailability()

    def cleanCustomNodes(self):
        '''
        removes all custom nodes from current scene
        '''
        LayerUtils.deleteCustomNodes()

        self.updateLayerAvailability()

    def getLayerName(self, id):
        return mel.eval('ngSkinLayer -id %d -q -name' % id)

    def setLayerName(self, id, name):
        cmds.ngSkinLayer(e=True, id=id, name=name)
        LayerEvents.nameChanged.emit()

    def getLayerOpacity(self, id):
        return mel.eval('ngSkinLayer -id %d -q -opacity' % id)

    def getLayerEnabled(self, id):
        return mel.eval('ngSkinLayer -id %d -q -enabled' % id)

    def setLayerEnabled(self, id, enabled):
        cmds.ngSkinLayer(e=True, id=id, enabled=1 if enabled else 0)

    def toggleLayerEnabled(self, id):
        self.setLayerEnabled(id, not self.getLayerEnabled(id))

    def getLayersCandidateFromSelection(self):
        '''
        for given selection, returns mesh and skin cluster node names where skinLayer data
        is (or can be) attached. 
        '''
        try:
            return cmds.ngSkinLayer(q=True, ldt=True)
        except:
            return []

    def getLayersAvailable(self):
        self.updateLayerAvailability()
        return self.layerDataAvailable
class LayerData(object):
    '''
    Intermediate data object between ngSkinTools core and importers/exporters,
    representing all layers info in one skin cluster. 
    '''
    def __init__(self):
        #: layers list
        self.layers = []
        self.mll = MllInterface()

        # a map [sourceInfluenceName] -> [destinationInfluenceName]
        self.mirrorInfluenceAssociationOverrides = {}

    def addMirrorInfluenceAssociationOverride(self,
                                              sourceInfluence,
                                              destinationInfluence=None,
                                              selfReference=False,
                                              bidirectional=True):
        '''
        Adds mirror influence association override, similar to UI of "Add influences association".
        Self reference creates a source<->source association, bidirectional means that destination->source 
        link is added as well
        '''

        if selfReference:
            self.mirrorInfluenceAssociationOverrides[
                sourceInfluence] = sourceInfluence
            return

        if destinationInfluence is None:
            raise MessageException("destination influence must be specified")

        self.mirrorInfluenceAssociationOverrides[
            sourceInfluence] = destinationInfluence

        if bidirectional:
            self.mirrorInfluenceAssociationOverrides[
                destinationInfluence] = sourceInfluence

    def addLayer(self, layer):
        '''
        register new layer into this data object
        '''
        assert isinstance(layer, Layer)
        self.layers.append(layer)

    @staticmethod
    def getFullNodePath(nodeName):
        result = cmds.ls(nodeName, l=True)
        if result is None or len(result) == 0:
            raise MessageException("node %s was not found" % nodeName)

        return result[0]

    def loadFrom(self, mesh):
        '''
        loads data from actual skin cluster and prepares it for exporting.
        supply skin cluster or skinned mesh as an argument
        '''

        self.mll.setCurrentMesh(mesh)

        for layerID, layerName in self.mll.listLayers():
            self.mirrorInfluenceAssociationOverrides = self.mll.listManualMirrorInfluenceAssociations(
            )

            layer = Layer()
            layer.name = layerName
            self.addLayer(layer)

            layer.opacity = self.mll.getLayerOpacity(layerID)
            layer.enabled = self.mll.isLayerEnabled(layerID)

            layer.mask = self.mll.getLayerMask(layerID)

            for inflName, logicalIndex in self.mll.listLayerInfluences(
                    layerID):
                influence = Influence()
                influence.influenceName = self.getFullNodePath(inflName)
                influence.logicalIndex = logicalIndex
                layer.addInfluence(influence)

                influence.weights = self.mll.getInfluenceWeights(
                    layerID, logicalIndex)

    def __validate(self):
        numVerts = self.mll.getVertCount()
        for layer in reversed(self.layers):
            maskLen = len(layer.mask)
            if maskLen != 0 and maskLen != numVerts:
                raise Exception(
                    "Invalid vertex count for mask in layer '%s': expected size is %d"
                    % (layer.name, numVerts))

            for influence in layer.influences:
                weightsLen = len(influence.weights)
                if weightsLen != numVerts:
                    raise Exception(
                        "Invalid weights count for influence '%s' in layer '%s': expected size is %d"
                        % (influence.influenceName, layer.name, numVerts))

                influence.logicalIndex = self.skinClusterFn.getLogicalInfluenceIndex(
                    influence.influenceName)

    @Utils.undoable
    def saveTo(self, mesh):
        '''
        saves data to actual skin cluster
        '''

        # set target to whatever was provided
        self.mll.setCurrentMesh(mesh)

        if not self.mll.getLayersAvailable():
            self.mll.initLayers()

        if not self.mll.getLayersAvailable():
            raise Exception("could not initialize layers")

        mesh, self.skinCluster = self.mll.getTargetInfo()
        self.skinClusterFn = SkinClusterFn()
        self.skinClusterFn.setSkinCluster(self.skinCluster)

        self.__validate()

        # set target to actual mesh
        self.mll.setCurrentMesh(mesh)

        with self.mll.batchUpdateContext():
            for source, destination in self.mirrorInfluenceAssociationOverrides:
                self.mll.addManualMirrorInfluenceAssociation(
                    source, destination)

            for layer in reversed(self.layers):
                layerId = self.mll.createLayer(name=layer.name,
                                               forceEmpty=True)
                self.mll.setCurrentLayer(layerId)
                if layerId is None:
                    raise Exception(
                        "import failed: could not create layer '%s'" %
                        (layer.name))

                self.mll.setLayerOpacity(layerId, layer.opacity)
                self.mll.setLayerEnabled(layerId, layer.enabled)
                self.mll.setLayerMask(layerId, layer.mask)

                for influence in layer.influences:
                    self.mll.setInfluenceWeights(layerId,
                                                 influence.logicalIndex,
                                                 influence.weights)

    def __repr__(self):
        return "[LayerDataModel(%r)]" % self.layers

    def getAllInfluences(self):
        '''
        a convenience method to retrieve a list of names of all influences used in this layer data object
        '''

        result = set()

        for layer in self.layers:
            for influence in layer.influences:
                result.add(influence.influenceName)

        return tuple(result)
Example #9
0
 def ensureTargetMeshLayers(self):
     targetMll = MllInterface(mesh=self.targetMesh)
     if not targetMll.getLayersAvailable():
         targetMll.initLayers()
Example #10
0
class LayerData(object):
    '''
    Intermediate data object between ngSkinTools core and importers/exporters,
    representing all layers info in one skin cluster. 
    '''
    
    def __init__(self):
        #: layers list
        self.layers = []
        self.mll = MllInterface()
        
        # a map [sourceInfluenceName] -> [destinationInfluenceName]
        self.mirrorInfluenceAssociationOverrides = {}
        
    def addMirrorInfluenceAssociationOverride(self,sourceInfluence,destinationInfluence=None,selfReference=False,bidirectional=True):
        '''
        Adds mirror influence association override, similar to UI of "Add influences association".
        Self reference creates a source<->source association, bidirectional means that destination->source 
        link is added as well
        '''
        
        if selfReference:
            self.mirrorInfluenceAssociationOverrides[sourceInfluence] = sourceInfluence
            return
        
        if destinationInfluence is None:
            raise MessageException("destination influence must be specified")
        
        self.mirrorInfluenceAssociationOverrides[sourceInfluence] = destinationInfluence
        
        if bidirectional:
            self.mirrorInfluenceAssociationOverrides[destinationInfluence] = sourceInfluence 
        

    def addLayer(self, layer):
        '''
        register new layer into this data object
        '''
        assert isinstance(layer, Layer)
        self.layers.append(layer)
       
    @staticmethod 
    def getFullNodePath(nodeName):
        result = cmds.ls(nodeName,l=True)
        if result is None or len(result)==0:
            raise MessageException("node %s was not found" % nodeName)
        
        return result[0]
        
    def loadFrom(self, mesh):
        '''
        loads data from actual skin cluster and prepares it for exporting.
        supply skin cluster or skinned mesh as an argument
        '''
        
        self.mll.setCurrentMesh(mesh)
        
        for layerID, layerName in self.mll.listLayers():
            self.mirrorInfluenceAssociationOverrides = self.mll.listManualMirrorInfluenceAssociations() 
            
            layer = Layer()
            layer.name = layerName
            self.addLayer(layer)
            
            
            layer.opacity = self.mll.getLayerOpacity(layerID)
            layer.enabled = self.mll.isLayerEnabled(layerID)
            
            layer.mask = self.mll.getLayerMask(layerID)
            
            for inflName, logicalIndex in self.mll.listLayerInfluences(layerID):
                influence = Influence()
                influence.influenceName = self.getFullNodePath(inflName)
                influence.logicalIndex = logicalIndex
                layer.addInfluence(influence)
                
                influence.weights = self.mll.getInfluenceWeights(layerID, logicalIndex)
                
    def __validate(self):
        numVerts = self.mll.getVertCount()
        for layer in reversed(self.layers):
            maskLen = len(layer.mask)
            if maskLen != 0 and maskLen != numVerts:
                raise Exception("Invalid vertex count for mask in layer '%s': expected size is %d" % (layer.name, numVerts))
            
            for influence in layer.influences:
                weightsLen = len(influence.weights) 
                if weightsLen != numVerts:
                    raise Exception("Invalid weights count for influence '%s' in layer '%s': expected size is %d" % (influence.influenceName, layer.name, numVerts))
                
                influence.logicalIndex = self.skinClusterFn.getLogicalInfluenceIndex(influence.influenceName)
                
        
    @Utils.undoable        
    def saveTo(self, mesh):
        '''
        saves data to actual skin cluster
        '''
        
        # set target to whatever was provided
        self.mll.setCurrentMesh(mesh)
        
        
        if not self.mll.getLayersAvailable():
            self.mll.initLayers()
            
        if not self.mll.getLayersAvailable():
            raise Exception("could not initialize layers")
        

        mesh, self.skinCluster = self.mll.getTargetInfo()
        self.skinClusterFn = SkinClusterFn()
        self.skinClusterFn.setSkinCluster(self.skinCluster)
        
        self.__validate()
        
        # set target to actual mesh
        self.mll.setCurrentMesh(mesh)
            
        with self.mll.batchUpdateContext():
            for source,destination in self.mirrorInfluenceAssociationOverrides.iteritems():
                self.mll.addManualMirrorInfluenceAssociation(source, destination)
            
            for layer in reversed(self.layers):
                layerId = self.mll.createLayer(name=layer.name, forceEmpty=True)
                self.mll.setCurrentLayer(layerId)
                if layerId is None:
                    raise Exception("import failed: could not create layer '%s'" % (layer.name))
                
                self.mll.setLayerOpacity(layerId, layer.opacity)
                self.mll.setLayerEnabled(layerId, layer.enabled)
                self.mll.setLayerMask(layerId, layer.mask)
                
                for influence in layer.influences:
                    self.mll.setInfluenceWeights(layerId, influence.logicalIndex, influence.weights)
        
                
    def __repr__(self):
        return "[LayerDataModel(%r)]" % self.layers
    
    def getAllInfluences(self):
        '''
        a convenience method to retrieve a list of names of all influences used in this layer data object
        '''
        
        result = set()
        
        for layer in self.layers:
            for influence in layer.influences:
                result.add(influence.influenceName)
                
        return tuple(result)
Example #11
0
from ngSkinTools.ui.events import Signal, LayerEvents, MayaEvents

log = getLogger('selectionState')

mll = MllInterface()

# container for mirror related state
mirrorInfo = CachedValue('mirrorInfo', lambda: {
    'axis': mll.getMirrorAxis(),
})

# container for primary selection data
selectionInfo = CachedValue(
    'selectionInfo', lambda: {
        'target': mll.getTargetInfo(),
        'layersAvailable': mll.getLayersAvailable(),
    })


def getLayersAvailable():
    return selectionInfo.get()['layersAvailable']


def bind(signals, values):
    for signal in signals:
        for value in values:
            signal.addHandler(value.dropCache)


def bindAll():
    log.info("adding handlers to signals")