コード例 #1
0
class CopyPasteActionsTest(AdditionalAsserts, unittest.TestCase):
    def setUp(self):
        testUtils.openMayaFile("normalization.ma")
        
        self.mll = MllInterface()
        cmds.select("testMesh")
        self.mll.initLayers()
        self.mll.createLayer("initial weights")
        self.layer = self.mll.createLayer("second layer")
        self.mll.setCurrentLayer(self.layer)
        
        self.clipboard = WeightsClipboard(self.mll)
        
        self.newWeights = [0.5]*self.mll.getVertCount()
        
    def tearDown(self):
        unittest.TestCase.tearDown(self)


    @insideMayaOnly
    def testCantCopyEmptyMask(self):
        with self.assertRaises(MessageException, "Nothing copied"):
            self.mll.setCurrentPaintTarget(LayerUtils.PAINT_TARGET_MASK)
            self.clipboard.withCurrentLayerAndInfluence().copy()

        # shold not be able to paste after this
        with self.assertRaises(MessageException, "Nothing to paste"):
            self.clipboard.withCurrentLayerAndInfluence().paste(True)
            

    @insideMayaOnly
    def testCantPasteIfNothingIsThere(self):
        with self.assertRaises(MessageException, "Nothing to paste"):
            self.clipboard.withCurrentLayerAndInfluence().paste(True)
        

    @insideMayaOnly
    def testCopyMask(self):
        # set mask to something
        self.mll.setLayerMask(self.layer,self.newWeights)
        self.assertArraysEqual(self.mll.getLayerMask(self.layer), self.newWeights)

        self.mll.setCurrentPaintTarget(LayerUtils.PAINT_TARGET_MASK)
        self.clipboard.withCurrentLayerAndInfluence().copy()
        
        self.assertArraysEqual(self.clipboard.copiedWeights, self.newWeights)
        
    @insideMayaOnly
    def testPasteMaskReplace(self):
        self.mll.setLayerMask(self.layer,self.newWeights)
        self.mll.setCurrentPaintTarget(LayerUtils.PAINT_TARGET_MASK)
        self.clipboard.copiedWeights = self.newWeights
        self.clipboard.withCurrentLayerAndInfluence().paste(True)
        self.assertArraysEqual(self.mll.getLayerMask(self.layer), self.newWeights)
        
    @insideMayaOnly
    def testPasteInfluenceReplace(self):
        influence = 1
        self.mll.setInfluenceWeights(self.layer, influence, self.newWeights)
        self.mll.setCurrentPaintTarget(influence)
        self.clipboard.copiedWeights = self.newWeights
        self.clipboard.withCurrentLayerAndInfluence().paste(True)
        self.assertArraysEqual(self.mll.getInfluenceWeights(self.layer,influence), self.newWeights)
コード例 #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)
コード例 #3
0
ファイル: importExport.py プロジェクト: leandropim/Tapp
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)
コード例 #4
0
ファイル: ng_extra_tools.py プロジェクト: AngryBaer/abMaya
class MapOperations(object):
    """ class that contains operations applied to the entire mesh """
    def __init__(self):
        self.mll = MllInterface()
        self.selection_name = []
        self.ngs_layer_id = -1
        self.ngs_influence = None
        self.ngs_weight_list = []
        self.ngs_vert_count = -1
        self.max_value = -1.0
        self.min_value = -1.0

    def get_data(self):
        self.selection_name = cmds.ls(selection=True)
        self.ngs_layer_id = self.mll.getCurrentLayer()
        self.ngs_influence = self.mll.getCurrentPaintTarget()
        self.ngs_weight_list = self.mll.getInfluenceWeights(
            self.ngs_layer_id, self.ngs_influence)
        self.ngs_vert_count = self.mll.getVertCount()
        self.max_value = max(self.ngs_weight_list)
        self.min_value = min(self.ngs_weight_list)

    def grow_map(self, intensity):
        """ pushes the border of the active weight map outwards """
        self.get_data()
        new_weight_list = []
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if vertex_weight >= self.max_value:
                new_weight_list.append(vertex_weight)
                continue
            vertex_name = '.'.join([self.selection_name[0], 'vtx[%d]' % i])
            area_vertices = get_surrounding_verts(vertex_name)
            weight_list = [self.ngs_weight_list[int(x)] for x in area_vertices]
            max_avg = max(weight_list)
            if max_avg <= self.min_value:
                new_weight_list.append(vertex_weight)
                continue
            grow_weight = vertex_weight + (abs(vertex_weight - max_avg) *
                                           intensity)
            grow_weight = min(grow_weight, self.max_value)
            new_weight_list.append(grow_weight)
        self.mll.setInfluenceWeights(self.ngs_layer_id, self.ngs_influence,
                                     new_weight_list)

    def shrink_map(self, intensity):
        """ pulls the border of the active weight map inwards """
        self.get_data()
        new_weight_list = []
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if vertex_weight <= self.min_value:
                new_weight_list.append(vertex_weight)
                continue
            vertex_name = '.'.join([self.selection_name[0], 'vtx[%d]' % i])
            area_vertices = get_surrounding_verts(vertex_name)
            weight_list = [self.ngs_weight_list[int(x)] for x in area_vertices]
            min_avg = min(weight_list)
            if min_avg >= self.max_value:
                new_weight_list.append(vertex_weight)
                continue
            shrink_weight = vertex_weight - (abs(vertex_weight - min_avg) *
                                             intensity)
            shrink_weight = max(shrink_weight, self.min_value)
            new_weight_list.append(shrink_weight)
        self.mll.setInfluenceWeights(self.ngs_layer_id, self.ngs_influence,
                                     new_weight_list)

    def conceal_map(self, intensity):
        """ smooth operation for the active map by only lowering values """
        self.get_data()
        new_weight_list = []
        threshold = 0.01 / (intensity / 0.1)
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if vertex_weight <= self.min_value:
                new_weight_list.append(vertex_weight)
                continue
            vertex_name = '.'.join([self.selection_name[0], 'vtx[%d]' % i])
            area_vertices = get_surrounding_verts(vertex_name)
            weight_list = [self.ngs_weight_list[int(x)] for x in area_vertices]
            weight_avg = sum(weight_list) / float(len(weight_list))
            min_avg = min(weight_list)
            if weight_avg >= self.max_value and abs(weight_avg -
                                                    vertex_weight) < threshold:
                new_weight_list.append(vertex_weight)
                continue
            weight_diff = abs(weight_avg - vertex_weight)
            conceal_weight = vertex_weight * (1 - (weight_diff * intensity))
            conceal_weight = max(conceal_weight, min_avg)
            new_weight_list.append(conceal_weight)
        self.mll.setInfluenceWeights(self.ngs_layer_id, self.ngs_influence,
                                     new_weight_list)

    def spread_map(self, intensity):
        """ smooth operation for the active map by only increasing values """
        self.get_data()
        new_weight_list = []
        threshold = 0.01 / (intensity / 0.1)
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if vertex_weight >= self.max_value:
                new_weight_list.append(vertex_weight)
                continue
            vertex_name = '.'.join([self.selection_name[0], 'vtx[%d]' % i])
            area_vertices = get_surrounding_verts(vertex_name)
            weight_list = [self.ngs_weight_list[int(x)] for x in area_vertices]
            weight_avg = sum(weight_list) / float(len(weight_list))
            max_avg = max(weight_list)
            if weight_avg <= self.min_value and abs(weight_avg -
                                                    vertex_weight) < threshold:
                new_weight_list.append(vertex_weight)
                continue
            weight_diff = abs(weight_avg - vertex_weight)
            spread_weight = vertex_weight + weight_diff * intensity
            spread_weight = min(spread_weight, max_avg)
            new_weight_list.append(spread_weight)
        self.mll.setInfluenceWeights(self.ngs_layer_id, self.ngs_influence,
                                     new_weight_list)

    def gain_map(self, intensity):
        """ 'reverse scale' tool that only increases weight values above 0 """
        self.get_data()
        new_weight_list = []
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if vertex_weight == 0:
                new_weight_list.append(vertex_weight)
                continue
            gain_weight = vertex_weight + (vertex_weight * intensity)
            gain_weight = min(gain_weight, 1)
            new_weight_list.append(gain_weight)
        self.mll.setInfluenceWeights(self.ngs_layer_id, self.ngs_influence,
                                     new_weight_list)

    def contrast_map(self, intensity):
        """ sharpens the edge of the active weight map """
        self.get_data()
        new_weight_list = []
        avg_value = (self.max_value + self.min_value) / 2
        normalized_range = self.max_value - self.min_value
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if not self.max_value > vertex_weight > self.min_value:
                new_weight_list.append(vertex_weight)
                continue
            dist_value = vertex_weight - avg_value
            modifier = norm.cdf(dist_value,
                                loc=0,
                                scale=(0.1 * normalized_range)) - vertex_weight
            contrast_weight = vertex_weight + modifier * intensity
            contrast_weight = max(self.min_value,
                                  min(contrast_weight, self.max_value))
            new_weight_list.append(contrast_weight)
        self.mll.setInfluenceWeights(self.ngs_layer_id, self.ngs_influence,
                                     new_weight_list)
コード例 #5
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:
                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)
コード例 #6
0
class MllInterfaceTest(AdditionalAsserts, unittest.TestCase):
    def setUp(self):
        unittest.TestCase.setUp(self)
        self.mll = MllInterface()

    @insideMayaOnly
    def testAccessForNoSelection(self):
        testUtils.openMayaFile("normalization.ma")
        cmds.select(cl=True)
        self.mll.setCurrentMesh(None)

        self.assertEquals(None, self.mll.getTargetInfo())

    @insideMayaOnly
    def testInvalidInfluenceIndex(self):
        testUtils.openMayaFile("normalization.ma")
        self.mll.setCurrentMesh('testMesh')
        self.mll.initLayers()
        layerId = self.mll.createLayer("initial weights")

        weights = [0.0] * self.mll.getVertCount()

        with self.assertRaises(
                errorMessage="skin cluster does not have logical influence 666"
        ):
            self.mll.setInfluenceWeights(layerId, 666, weights)

    @insideMayaOnly
    def testAccess(self):
        # test the same for specified mesh, or None for current selection
        for mesh in ('testMesh', None):
            testUtils.openMayaFile("normalization.ma")
            self.mll.setCurrentMesh(mesh)
            if mesh is None:
                cmds.select('testMesh')

            self.mll.initLayers()
            layerId = self.mll.createLayer("initial weights")
            self.assertNotEqual(None, layerId)

            self.assertEqual("initial weights", self.mll.getLayerName(layerId))

            self.assertEqual(16, self.mll.getVertCount(),
                             "Invalid vertex count detected")

            self.assertEqual(1, self.mll.getLayerOpacity(layerId))
            self.assertEqual(True, self.mll.isLayerEnabled(layerId))
            self.assertEqual([], self.mll.getLayerMask(layerId))
            self.assertFloatArraysEqual(
                self.mll.getInfluenceWeights(layerId, 1), [
                    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0,
                    1.0, 1.0, 1.0, 1.0
                ])

    @insideMayaOnly
    def testOverwriteWeightsCrashesMaya(self):
        testUtils.openMayaFile("genericSkinnedMesh.mb")
        self.mll.setCurrentMesh("mesh|pPlane1")
        self.mll.initLayers()
        layerId = self.mll.createLayer("initial weights", forceEmpty=True)
        weights = [1.0] * self.mll.getVertCount()
        weights[0] = 0.7
        weights[1] = 0.5
        weights[2] = 0.3

        self.mll.setInfluenceWeights(layerId, 0, weights)
        self.assertFloatArraysEqual(
            self.mll.getInfluenceWeights(layerId, 0)[0:3], [0.7, 0.5, 0.3])

        self.mll.setInfluenceWeights(layerId, 1, weights)
        self.assertFloatArraysEqual(weights,
                                    self.mll.getInfluenceWeights(layerId, 1))

        # previous influence should have some of it's value substracted
        self.assertFloatArraysEqual(
            self.mll.getInfluenceWeights(layerId, 0)[0:3], [0.3, 0.5, 0.3])

    @insideMayaOnly
    def testAddManualMirrorInfluences(self):
        testUtils.openMayaFile("genericSkinnedMesh.mb")
        self.mll.setCurrentMesh("mesh|pPlane1")
        self.mll.initLayers()
        layerId = self.mll.createLayer("initial weights")

        result = self.mll.listManualMirrorInfluenceAssociations()
        self.assertArraysEqual(result, [])

        self.mll.addManualMirrorInfluenceAssociation("L_joint1", "R_joint1")
        self.mll.addManualMirrorInfluenceAssociation("R_joint1", "L_joint1")

        result = self.mll.listManualMirrorInfluenceAssociations()
        self.assertDictionariesEqual(result, {
            "L_joint1": "R_joint1",
            "R_joint1": "L_joint1"
        })

    @insideMayaOnly
    def testSetMaxInfluencePerVertex(self):
        testUtils.openMayaFile("genericSkinnedMesh.mb")
        self.mll.setCurrentMesh("mesh|pPlane1")
        self.mll.initLayers()
        layerId = self.mll.createLayer("initial weights")

        self.assertEqual(self.mll.getInfluenceLimitPerVertex(), 0,
                         "initial limit per vertex should be zero")

        self.mll.setInfluenceLimitPerVertex(10)
        self.assertEqual(self.mll.getInfluenceLimitPerVertex(), 10,
                         "was not able to set limit to the desired value")

        self.mll.setInfluenceLimitPerVertex(2)
        self.assertEqual(self.mll.getInfluenceLimitPerVertex(), 2,
                         "was not able to set limit to the desired value")
コード例 #7
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)
コード例 #8
0
ファイル: ng_equalizer.py プロジェクト: AngryBaer/abMaya
class Equalize(object):
    def __init__(self):
        self.mll = MllInterface()
        self.ngs_vert_count = -1
        self.ngs_layer_id = -1
        self.intensity = -1
        self.ngs_influence = None
        self.vert_weight_dict = {}
        self.vert_weight_list = []
        self.ngs_weight_dict = {}
        self.ngs_weight_list = []
        self.new_weight_list = []
        self.selection_vert = []
        self.vertex_list = []
        self.id_list = []

        self.modes = {
            "max": self.vert_max,
            "min": self.vert_min,
            "avg": self.vert_avg,
            "first": self.vert_first,
            "last": self.vert_last
        }

    def get_data(self, check):
        self.ngs_weight_dict = {}
        self.vert_weight_dict = {}
        self.selection_vert = cmds.ls(os=True)
        if not self.selection_vert:
            return False
        if not check:
            self.ngs_influence = [("selected influence:",
                                   self.mll.getCurrentPaintTarget())]
        else:
            self.ngs_influence = self.mll.listLayerInfluences(
                layerId=None, activeInfluences=True)
        self.mll = MllInterface()
        self.ngs_layer_id = self.mll.getCurrentLayer()
        self.ngs_vert_count = self.mll.getVertCount()
        self.vertex_list = cmds.ls(self.selection_vert, flatten=True)
        self.id_list = list(
            map(lambda x: x[x.find("[") + 1:x.find("]")], self.vertex_list))
        for influences in self.ngs_influence:
            influence_weights = self.mll.getInfluenceWeights(
                self.ngs_layer_id, influences[1])
            vert_weights = [influence_weights[int(i)] for i in self.id_list]
            self.ngs_weight_dict[influences] = influence_weights
            self.vert_weight_dict[influences] = vert_weights
        return True

    def set_vert(self, mode, intensity):
        self.intensity = intensity
        for influences in self.ngs_influence:
            self.new_weight_list = []
            self.ngs_weight_list = self.ngs_weight_dict[influences]
            self.vert_weight_list = self.vert_weight_dict[influences]
            self.modes[mode]()
            self.mll.setInfluenceWeights(self.ngs_layer_id, influences[1],
                                         self.new_weight_list)

    def vert_max(self):
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if str(i) not in self.id_list or vertex_weight == max(
                    self.vert_weight_list):
                self.new_weight_list.append(vertex_weight)
                continue
            new_weight = vertex_weight + (
                (max(self.vert_weight_list) - vertex_weight) * self.intensity)
            self.new_weight_list.append(new_weight)

    def vert_min(self):
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if str(i) not in self.id_list or vertex_weight == min(
                    self.vert_weight_list):
                self.new_weight_list.append(vertex_weight)
                continue
            new_weight = vertex_weight - (
                (vertex_weight - min(self.vert_weight_list)) * self.intensity)
            self.new_weight_list.append(new_weight)

    def vert_avg(self):
        average = sum(self.vert_weight_list) / float(len(
            self.vert_weight_list))
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if str(i) not in self.id_list or vertex_weight == average:
                self.new_weight_list.append(vertex_weight)
                continue
            new_weight = vertex_weight - (
                (vertex_weight - average) * self.intensity)
            self.new_weight_list.append(new_weight)

    def vert_first(self):
        first = self.vert_weight_list[0]
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if str(i) not in self.id_list or vertex_weight == first:
                self.new_weight_list.append(vertex_weight)
                continue
            new_weight = vertex_weight - (
                (vertex_weight - first) * self.intensity)
            self.new_weight_list.append(new_weight)

    def vert_last(self):
        last = self.vert_weight_list[-1]
        for i in range(self.ngs_vert_count):
            vertex_weight = self.ngs_weight_list[i]
            if str(i) not in self.id_list or vertex_weight == last:
                self.new_weight_list.append(vertex_weight)
                continue
            new_weight = vertex_weight - (
                (vertex_weight - last) * self.intensity)
            self.new_weight_list.append(new_weight)
コード例 #9
0
class MllInterfaceTest(AdditionalAsserts, unittest.TestCase):
    
    def setUp(self):
        unittest.TestCase.setUp(self)
        self.mll = MllInterface()
        

    @insideMayaOnly
    def testAccessForNoSelection(self):
        testUtils.openMayaFile("normalization.ma")
        cmds.select(cl=True)
        self.mll.setCurrentMesh(None)
        
        self.assertEquals(None,self.mll.getTargetInfo())

    @insideMayaOnly
    def testInvalidInfluenceIndex(self):
        testUtils.openMayaFile("normalization.ma")
        self.mll.setCurrentMesh('testMesh')
        self.mll.initLayers()
        layerId = self.mll.createLayer("initial weights")
        
        weights = [0.0]*self.mll.getVertCount()
        
        with self.assertRaises(errorMessage="skin cluster does not have logical influence 666"):
            self.mll.setInfluenceWeights(layerId, 666, weights)
            
    
    @insideMayaOnly
    def testAccess(self):
        # test the same for specified mesh, or None for current selection
        for mesh in ('testMesh',None):
            testUtils.openMayaFile("normalization.ma")
            self.mll.setCurrentMesh(mesh)
            if mesh is None:
                cmds.select('testMesh')
                            
            self.mll.initLayers()
            layerId = self.mll.createLayer("initial weights")
            self.assertNotEqual(None, layerId)
            
            self.assertEqual("initial weights",self.mll.getLayerName(layerId))
            
            self.assertEqual(16, self.mll.getVertCount(), "Invalid vertex count detected")
            
            self.assertEqual(1, self.mll.getLayerOpacity(layerId))
            self.assertEqual(True, self.mll.isLayerEnabled(layerId))
            self.assertEqual([], self.mll.getLayerMask(layerId))
            self.assertFloatArraysEqual(self.mll.getInfluenceWeights(layerId,1),[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0])
            
    @insideMayaOnly
    def testOverwriteWeightsCrashesMaya(self):
        testUtils.openMayaFile("genericSkinnedMesh.mb")
        self.mll.setCurrentMesh("mesh|pPlane1")
        self.mll.initLayers()
        layerId = self.mll.createLayer("initial weights",forceEmpty=True)
        weights = [1.0]*self.mll.getVertCount()
        weights[0] = 0.7
        weights[1] = 0.5
        weights[2] = 0.3
        
        self.mll.setInfluenceWeights(layerId, 0, weights)
        self.assertFloatArraysEqual(self.mll.getInfluenceWeights(layerId, 0)[0:3], [0.7,0.5,0.3])

        self.mll.setInfluenceWeights(layerId, 1, weights)
        self.assertFloatArraysEqual(weights,self.mll.getInfluenceWeights(layerId, 1))
        
        # previous influence should have some of it's value substracted
        self.assertFloatArraysEqual(self.mll.getInfluenceWeights(layerId, 0)[0:3], [0.3,0.5,0.3])
        
        
        
    @insideMayaOnly
    def testAddManualMirrorInfluences(self):
        testUtils.openMayaFile("genericSkinnedMesh.mb")
        self.mll.setCurrentMesh("mesh|pPlane1")
        self.mll.initLayers()
        layerId = self.mll.createLayer("initial weights")
        
        result = self.mll.listManualMirrorInfluenceAssociations()
        self.assertArraysEqual(result, [])
        
        self.mll.addManualMirrorInfluenceAssociation("L_joint1", "R_joint1")
        self.mll.addManualMirrorInfluenceAssociation("R_joint1", "L_joint1")
        
        result = self.mll.listManualMirrorInfluenceAssociations()
        self.assertDictionariesEqual(result, {"L_joint1":"R_joint1","R_joint1":"L_joint1"})
        

    @insideMayaOnly
    def testSetMaxInfluencePerVertex(self):
        testUtils.openMayaFile("genericSkinnedMesh.mb")
        self.mll.setCurrentMesh("mesh|pPlane1")
        self.mll.initLayers()
        layerId = self.mll.createLayer("initial weights")
        
        self.assertEqual(self.mll.getInfluenceLimitPerVertex(),0,"initial limit per vertex should be zero")

        self.mll.setInfluenceLimitPerVertex(10)
        self.assertEqual(self.mll.getInfluenceLimitPerVertex(),10,"was not able to set limit to the desired value")

        self.mll.setInfluenceLimitPerVertex(2)
        self.assertEqual(self.mll.getInfluenceLimitPerVertex(),2,"was not able to set limit to the desired value")