コード例 #1
0
    def execute(self):
        targetMll = MllInterface(mesh=self.targetMesh)

        self.ensureTargetMeshLayers()

        previousLayerIds = [layer[0] for layer in targetMll.listLayers()]

        sourceMesh = self.sourceMesh
        if self.sourceModel is not None:
            self.sourceModel.saveTo(MllInterface.TARGET_REFERENCE_MESH)
            sourceMesh = MllInterface.TARGET_REFERENCE_MESH

        vertexTransferMode = self.vertexTransferModes[
            self.parent.controls.transferMode.getSelectedText()]

        sourceMll = MllInterface(mesh=sourceMesh)

        with targetMll.batchUpdateContext():
            sourceMll.transferWeights(self.targetMesh,
                                      influencesMapping=self.mapper.mapping,
                                      vertexTransferMode=vertexTransferMode)

            if self.parent.controls.keepExistingLayers.getValue() != 1:
                for layerId in previousLayerIds:
                    targetMll.deleteLayer(layerId)

        LayerDataModel.getInstance().updateLayerAvailability()
        LayerEvents.layerListModified.emit()
コード例 #2
0
    def setUp(self):
        self.targetMesh = 'testMesh'
        self.numVerts = 169  # number of verts in our mesh
        self.mll = MllInterface()

        unittest.TestCase.setUp(self)
        openMayaFile('simplemirror.ma')
        cmds.hide("|y_axis")
        cmds.showHidden("|x_axis")

        self.mll.setCurrentMesh(self.targetMesh)
        self.mll.initLayers()
        self.mll.createLayer("original weights")

        self.model = ie.LayerData()
        self.layer = ie.Layer()
        self.layer.opacity = 1.0
        self.layer.enabled = True
        self.layer.mask = [0.0] * self.numVerts
        self.layer.mask[1] = 0.9
        self.layer.mask[5] = 0.5566
        self.layer.name = "imported layer"
        self.model.addLayer(self.layer)

        self.infl = ie.Influence()
        self.layer.addInfluence(self.infl)
        self.infl.influenceName = "x_axis|root|R_joint1"
        self.infl.logicalIndex = 666  # use nonsense value
        self.infl.weights = [0.1] * self.numVerts
        self.infl.weights[3] = 0.688
        self.infl.weights[4] = 0.345
コード例 #3
0
 def updateConfiguration(self):
     mll = MllInterface()
     mll.configureVertexMirrorMapping(
         mirrorAxis=self.mirrorAxis.getSelectedText(),
         vertexTransferMode=self.vertexTransferModes[
             self.transferMode.getSelectedText()])
     LayerEvents.mirrorConfigurationChanged.emit()
コード例 #4
0
    def __init__(self):
        #: layers list
        self.layers = []
        self.mll = MllInterface()

        # a map [sourceInfluenceName] -> [destinationInfluenceName]
        self.mirrorInfluenceAssociationOverrides = {}
コード例 #5
0
ファイル: ng_equalizer.py プロジェクト: AngryBaer/abMaya
 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
コード例 #6
0
ファイル: ng_extra_tools.py プロジェクト: AngryBaer/abMaya
 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
コード例 #7
0
 def __detectSkinCluster(self,mesh):
     mll = MllInterface()
     mll.setCurrentMesh(mesh)
     try:
         _,skinCluster = mll.getTargetInfo()
     except TypeError:
         raise MessageException("cannot find skin cluster attached to %s" % mesh)
     
     log.info("detected skin cluster %s on mesh %s" % (skinCluster, mesh))
     return skinCluster
コード例 #8
0
 def __detectSkinCluster(self,mesh):
     mll = MllInterface()
     mll.setCurrentMesh(mesh)
     try:
         _,skinCluster = mll.getTargetInfo()
     except TypeError:
         raise MessageException("cannot find skin cluster attached to %s" % mesh)
     
     log.info("detected skin cluster %s on mesh %s" % (skinCluster, mesh))
     return skinCluster
コード例 #9
0
 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()
コード例 #10
0
    def __init__(self):
        self.layers = []
        self.mll = MllInterface()

        self.meshInfo = MeshInfo()

        self.influences = []

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

        self.skinClusterFn = None
コード例 #11
0
 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()
コード例 #12
0
    def setUp(self):
        unittest.TestCase.setUp(self)

        self.mll = MllInterface()

        self.setup = DuplicateLayers()
        self.setup.setMllInterface(self.mll)

        testUtils.openMayaFile("influence transfer.mb")

        self.mll.setCurrentMesh('sourceMesh')

        self.mll.initLayers()
コード例 #13
0
ファイル: ng_extra_tools.py プロジェクト: AngryBaer/abMaya
    def __init__(self, surface_name):
        self.surface = surface_name
        self.stroke_id = None
        self.mode = 1
        self.value = 1
        self.volumeThreshold = -0.1

        self.mll = MllInterface()
        self.selection_name = cmds.ls(selection=True)
        self.mesh = cmds.listRelatives(self.selection_name[0], shapes=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.max_value = max(self.ngs_weight_list)
        self.min_value = min(self.ngs_weight_list)
コード例 #14
0
    def setUp(self):
        self.targetMesh = 'testMesh' 
        self.numVerts = 169 # number of verts in our mesh
        self.mll = MllInterface()
        
        unittest.TestCase.setUp(self)
        openMayaFile('simplemirror.ma')
        cmds.hide("|y_axis")
        cmds.showHidden("|x_axis")
    
        self.mll.setCurrentMesh(self.targetMesh)
        self.mll.initLayers()
        self.mll.createLayer("original weights")
        
        self.model = ie.LayerData()
        self.layer = ie.Layer()
        self.layer.opacity = 1.0
        self.layer.enabled = True
        self.layer.mask = [0.0]*self.numVerts
        self.layer.mask[1] = 0.9 
        self.layer.mask[5] = 0.5566 
        self.layer.name = "imported layer"
        self.model.addLayer(self.layer)

        self.infl = ie.Influence()
        self.layer.addInfluence(self.infl)
        self.infl.influenceName = "x_axis|root|R_joint1"
        self.infl.logicalIndex = 666 # use nonsense value
        self.infl.weights = [0.1]*self.numVerts
        self.infl.weights[3] = 0.688
        self.infl.weights[4] = 0.345
コード例 #15
0
 def __init__(self):
     #: layers list
     self.layers = []
     self.mll = MllInterface()
     
     # a map [sourceInfluenceName] -> [destinationInfluenceName]
     self.mirrorInfluenceAssociationOverrides = {}
コード例 #16
0
ファイル: initTransferWindow.py プロジェクト: leandropim/Tapp
 def execute(self):
     influencesMapping = MllInterface.influencesMapToList(self.mapper.mapping)
     mirrorAxis = TransferWeightsTab.axisValues[self.mapper.distanceMatchRule.mirrorAxis]
     
     cmds.ngSkinLayer(initMirrorData=True, influencesMapping=influencesMapping, mirrorAxis=mirrorAxis)
     
     LayerDataModel.getInstance().mll.setManualMirrorInfluences(self.mapper.manualOverrides)
     
     LayerDataModel.getInstance().updateMirrorCacheStatus()
コード例 #17
0
 def testLoad2(self):
     openMayaFile('simplemirror.ma')
     mll = MllInterface()
     mll.setCurrentMesh('testMesh')
     mll.initLayers()
     mll.createLayer("Initial Weights")
     
     model = LayerData()
     model.loadFrom('testMesh')
     
     self.assertEqual(model.layers[0].name, "Initial Weights")
     self.assertEquals(model.layers[0].influences[0].influenceName, "|x_axis|root")
コード例 #18
0
ファイル: removeLayerData.py プロジェクト: lazerdaze/lancer
def removeLayersFromSelection():
    mll = MllInterface()
    target = mll.getTargetInfo()
    if target is None:
        return

    def asList(arg):
        if arg is None:
            return []
        return arg


    # delete any ngSkinTools deformers from the history, and find upstream stuff from given skinCluster.
    mesh,skinCluster = target
    hist = asList(cmds.listHistory(mesh))+asList(cmds.listHistory(skinCluster,future=True,levels=1))

    cmds.delete([i for i in hist if cmds.nodeType(i) in ('ngSkinLayerDisplay','ngSkinLayerData')])

    LayerDataModel.getInstance().updateLayerAvailability()
    selectionState.selectionInfo.dropCache()
コード例 #19
0
ファイル: layerDataModel.py プロジェクト: seokkwan/Tapp
 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()
コード例 #20
0
ファイル: importExport.py プロジェクト: leandropim/Tapp
    def __init__(self):
        self.layers = []
        self.mll = MllInterface()

        self.meshInfo = MeshInfo()
        
        self.influences = []
        
        # a map [sourceInfluenceName] -> [destinationInfluenceName]
        self.mirrorInfluenceAssociationOverrides = None
        
        self.skinClusterFn = None
コード例 #21
0
 def testAddManualInfluence(self):
     openMayaFile('simplemirror.ma')
     cmds.select("testMeshY")
     mll = MllInterface()
     mll.initLayers()
     mll.setCurrentLayer(mll.createLayer("test layer"))
     
     
     window = MainWindow.open()
     mirrorTab = window.findTab(TabMirror)
     
     initWindow = mirrorTab.execInitMirror()
     
     def selectPairAndClickOk(dialog):
         log.info("running inside modal dialog "+str(dialog))
         dialog.sourceDropdown.setValue(2)
         dialog.destinationDropdown.setValue(3)
         dialog.chkSelfReference.setValue(False)
         #dialog.closeDialogWithResult(BaseDialog.BUTTON_OK)
         closeNextDialogWithResult(BaseDialog.BUTTON_OK)
         
     log.info("modal dialog setup")
     runInNextModalDialog(selectPairAndClickOk)
     log.info("modal dialog setup ended")
         
     initWindow.content.addPairAction.execute()
     
     self.assertEquals(str(initWindow.content.items[0]),"[M] L_joint2 <-> L_joint3", "manual pair addition failed")
コード例 #22
0
    def buildInfluenceMappingEngine(self, controls):
        '''
        builds influence transfer mapping, using parameters from UI
        '''

        mapping = TransferDataModel.buildInfluenceMappingEngine(self, controls)

        mapping.nameMatchRule.ignoreNamespaces = self.parent.controls.ignoreNamespaces.getValue(
        ) == 1

        mapping.rules = [mapping.distanceMatchRule, mapping.nameMatchRule]
        if self.sourceModel is not None:
            mapping.sourceInfluences = self.sourceModel.influences
        else:
            mapping.sourceInfluences = MllInterface(
                mesh=self.sourceMesh).listInfluenceInfo()

        mapping.destinationInfluences = MllInterface(
            mesh=self.targetMesh).listInfluenceInfo()

        mapping.mirrorMode = False
        return mapping
コード例 #23
0
 def setUp(self):
     unittest.TestCase.setUp(self)
     
     self.mll = MllInterface()
     
     self.setup = DuplicateLayers()
     self.setup.setMllInterface(self.mll)
     
     testUtils.openMayaFile("influence transfer.mb")
     
     self.mll.setCurrentMesh('sourceMesh')
     
     self.mll.initLayers()
コード例 #24
0
ファイル: ng_equalizer.py プロジェクト: AngryBaer/abMaya
    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
        }
コード例 #25
0
 def testLoad2(self):
     openMayaFile('simplemirror.ma')
     mll = MllInterface()
     mll.setCurrentMesh('testMesh')
     mll.initLayers()
     mll.createLayer("Initial Weights")
     
     model = LayerData()
     model.loadFrom('testMesh')
     
     self.assertEqual(model.layers[0].name, "Initial Weights")
     self.assertEquals(model.layers[0].influences[0].influenceName, "|x_axis|root")
コード例 #26
0
    def execute(self):
        influencesMapping = MllInterface.influencesMapToList(
            self.mapper.mapping)
        mirrorAxis = TransferWeightsTab.axisValues[
            self.mapper.distanceMatchRule.mirrorAxis]

        cmds.ngSkinLayer(initMirrorData=True,
                         influencesMapping=influencesMapping,
                         mirrorAxis=mirrorAxis)

        LayerDataModel.getInstance().mll.setManualMirrorInfluences(
            self.mapper.manualOverrides)

        LayerDataModel.getInstance().updateMirrorCacheStatus()
コード例 #27
0
    def buildInfluenceMappingEngine(self, controls):
        '''
        builds influence transfer mapping, using parameters from UI
        '''

        mapper = self.mapper = InfluenceMapping()
        mapper.mirrorMode = False
        self.mapper.distanceMatchRule.maxThreshold = float(
            controls.influenceDistanceError.getValue())

        mapper.nameMatchRule.ignoreNamespaces = controls.ignoreNamespaces.getValue(
        ) == 1

        mapper.rules = [mapper.distanceMatchRule, mapper.nameMatchRule]
        if self.sourceModel is not None:
            mapper.sourceInfluences = self.sourceModel.influences
        else:
            mapper.sourceInfluences = MllInterface(
                mesh=self.sourceMesh).listInfluenceInfo()

        mapper.destinationInfluences = MllInterface(
            mesh=self.targetMesh).listInfluenceInfo()

        return mapper
コード例 #28
0
ファイル: copySkinLayers.py プロジェクト: darkuress/arFace
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)
コード例 #29
0
    def setUp(self):
        unittest.TestCase.setUp(self)
        testUtils.openMayaFile("normalization.ma")
        self.mll = mll = MllInterface()
        mll.setCurrentMesh("testMesh")
        mll.initLayers()
        mll.createLayer("initial weights")
        mll.createLayer("second layer")

        HeadlessDataHost.HANDLE.addReference(self)

        self.exportName = os.path.join(
            tempfile.gettempdir(),
            'layersTestData_%d.xml' % random.randint(0, 1024 * 1024))
        self.assertTrue(not os.path.exists(self.exportName),
                        'target file already exists')
コード例 #30
0
def initLayers(bndGrp, mesh):
    '''
    first step in setup
    bind bnds in bndGrp to mesh
    and set up ngSkinLayers
    '''
    # init skin layers
    mll = MllInterface()
    mll.setCurrentMesh(mesh.name())
    mll.initLayers()
    
    return mll
コード例 #31
0
ファイル: actions.py プロジェクト: lazerdaze/lancer
    def execute(self):
        meshes = cmds.ls(sl=True)
        if meshes == None or len(meshes) != 2:
            raise MessageException(
                "select two skinned meshes with layers initialized to perform this operation"
            )

        for mesh in meshes:
            if not MllInterface(mesh=meshes[0]).getLayersAvailable():
                raise MessageException(
                    "'%s' is not a valid selection (no skin layers available)"
                    % mesh)

        from ngSkinTools.ui.initTransferWindow import TransferWeightsWindow
        window = TransferWeightsWindow.getInstance()
        window.showWindow()
        window.content.dataModel.setSourceMesh(meshes[0])
        window.content.dataModel.setDestinationMesh(meshes[1])
コード例 #32
0
ファイル: tabPaint.py プロジェクト: lazerdaze/lancer
    def configurePaintValues(self):
        '''
        sets paint tool values from UI
        '''
        oper, pvalue = self.getPaintModeValues()
        cmds.ngSkinLayer(paintOperation=oper,
                         paintIntensity=pvalue.get(),
                         paintMirror=self.controls.mirroredMode.isChecked())

        from ngSkinTools import selectionState
        if self.controls.mirroredMode.isChecked(
        ) and selectionState.getLayersAvailable():
            self.parentWindow.tabMirror.influenceMappingConfiguration.updateSelectionsInfluenceMapping(
            )

        MllInterface().setPaintOptionRedistributeWeight(
            self.controls.redistributeWeightSetting.isChecked())

        if cmds.artUserPaintCtx(self.TOOL_PAINT, exists=True):
            # internally, use maya's "replace" brush with intensity of 1.0
            cmds.artUserPaintCtx(
                self.TOOL_PAINT,
                e=True,
                selectedattroper='absolute',
                value=1.0,
                opacity=1.0,
                brushfeedback=False,
                accopacity=False,
                usepressure=self.controls.stylusPressureOnOff.isChecked(),
                stampProfile=self.getSelectedBrushShape())

            if self.controls.stylusPressureOnOff.isChecked():
                try:
                    cmds.artUserPaintCtx(self.TOOL_PAINT,
                                         e=True,
                                         mappressure=self.controls.
                                         stylusPressureMode.getSelectedText())
                except:
                    pass

        self.updateToTool()
コード例 #33
0
    def testAddInfluence(self):
        self.importer.setSourceFromMesh("sourceMesh")
        self.importer.setDestinationFromMesh("destinationMesh")
        self.importer.addInfluence("joint3")

        influences = self.importer.listInfluencesDiff()
        self.assertArraysEqual(influences,  ["|joint1"])
        
        skinCluster = SkinClusterFn()
        skinCluster.setSkinCluster(self.importer.destinationSkinCluster)
        influenceLogicalIndex = skinCluster.getLogicalInfluenceIndex("joint3")
        
        mll = MllInterface()
        mll.setCurrentMesh("destinationMesh")
        mll.initLayers()
        layerId = mll.createLayer("test")
        weights = mll.getInfluenceWeights(layerId, influenceLogicalIndex)
        print weights
        self.assertFloatArraysEqual(weights, [0.0]*25)
コード例 #34
0
    def testDefectXmlImportIssue(self):
        openMayaFile("defect-xml-import-fails/mirror_association_issues.ma")

        importer = XmlImporter()
        data = importer.process(getTestFileContents("defect-xml-import-fails/mirror_weighting_manual_layers.xml"))
        
        
        mll = MllInterface()
        mll.setCurrentMesh("pSphere1")
        
        
        overrides = mll.listManualMirrorInfluenceAssociations()
        self.assertEqual(len(overrides), 0)
        
        data.saveTo("pSphere1")
        
        overrides = mll.listManualMirrorInfluenceAssociations()
        self.assertEqual(len(overrides), 1)
コード例 #35
0
ファイル: initTransferWindow.py プロジェクト: leandropim/Tapp
    def execute(self):
        targetMll = MllInterface(mesh=self.targetMesh)

        self.ensureTargetMeshLayers()
        
        previousLayerIds = [layerId for layerId, _  in targetMll.listLayers()]

        sourceMesh = self.sourceMesh        
        if self.sourceModel is not None:
            self.sourceModel.saveTo(MllInterface.TARGET_REFERENCE_MESH)
            sourceMesh = MllInterface.TARGET_REFERENCE_MESH
        
        vertexTransferMode = self.vertexTransferModes[self.parent.controls.transferMode.getSelectedText()]

        sourceMll = MllInterface(mesh=sourceMesh)
        sourceMll.transferWeights(self.targetMesh,influencesMapping=self.mapper.mapping,vertexTransferMode=vertexTransferMode)

        if self.parent.controls.keepExistingLayers.getValue()!=1:
            for layerId in previousLayerIds:
                targetMll.deleteLayer(layerId)

        LayerDataModel.getInstance().updateLayerAvailability()
        LayerEvents.layerListModified.emit()
コード例 #36
0
ファイル: nc_deform.py プロジェクト: nilusss/autoRigger
def save_weights(weight_dir, geo_list=[]):

    """
    save geometry weights for character
    """

    for obj in geo_list:
        # save dir and save file

        weight_file = os.path.join(weight_dir, obj + '.json')

        layerData = LayerData()
        try:
            layerData.loadFrom(obj + 'Shape')
        except:
            try:
                mll = MllInterface()
                mll.setCurrentMesh(obj+'Shape')
                ass = mll.initLayers()
                layer = mll.createLayer('Base Weights')
            except:
                pass
        exporter = JsonExporter()
        jsonContents = exporter.process(layerData)
        # string "jsonContents" can now be saved to an external file

        with open(weight_file, 'w') as f:
            f.write(jsonContents)
            #json.dump(jsonContents, f)
        # save skin weight file

        #mc.select(obj)
        #bSkinSaver2.bSaveSkinValues(weight_file)

        print "Saved to: " + weight_file
        mc.select(d=True)
コード例 #37
0
class DuplicateLayersTest(AdditionalAsserts, unittest.TestCase):
    def setUp(self):
        unittest.TestCase.setUp(self)

        self.mll = MllInterface()

        self.setup = DuplicateLayers()
        self.setup.setMllInterface(self.mll)

        testUtils.openMayaFile("influence transfer.mb")

        self.mll.setCurrentMesh('sourceMesh')

        self.mll.initLayers()

    def testLayerName(self):
        self.assertEquals("layer1 copy", self.setup.createLayerName("layer1"))
        self.assertEquals("layer1 copy(2)",
                          self.setup.createLayerName("layer1 copy"))
        self.assertEquals("layer1 copy(1000001)",
                          self.setup.createLayerName("layer1 copy(1000000)"))

    @insideMayaOnly
    def testDuplicateOneLayer(self):
        id = self.mll.createLayer("layer1 copy")
        self.setup.addLayer(id)
        self.setup.execute()

        newId = self.setup.duplicateIds[0]
        self.assertEqual(self.mll.getLayerName(newId), 'layer1 copy(2)')
        self.assertEqual(self.mll.isLayerEnabled(newId), True)
        self.assertAlmostEqual(self.mll.getLayerOpacity(newId), 1.0)
        self.assertFloatArraysEqual(self.mll.getLayerMask(newId), [])

        self.assertFloatArraysEqual(self.mll.getInfluenceWeights(id, 1),
                                    self.mll.getInfluenceWeights(newId, 1))

    @insideMayaOnly
    def testLayerOrder(self):
        id1 = self.mll.createLayer("layer1")
        id2 = self.mll.createLayer("layer2")

        def layerNames():
            return [a[1] for a in self.mll.listLayers()]

        self.assertArraysEqual(layerNames(), ["layer2", "layer1"])

        self.setup.addLayer(id1)
        self.setup.addLayer(id2)

        self.setup.execute()

        # order respects "newer layers on top"
        self.assertArraysEqual(
            layerNames(), ["layer2 copy", "layer1 copy", "layer2", "layer1"])

        # individual indexes in copy array respect indexes in the original array
        self.assertEquals(self.mll.getLayerName(self.setup.duplicateIds[0]),
                          "layer1 copy")
        self.assertEquals(self.mll.getLayerName(self.setup.duplicateIds[1]),
                          "layer2 copy")
コード例 #38
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")
        
        
コード例 #39
0
 def setUp(self):
     unittest.TestCase.setUp(self)
     self.mll = MllInterface()
コード例 #40
0
ファイル: layerDataModel.py プロジェクト: seokkwan/Tapp
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    
コード例 #41
0
ファイル: layerDataModel.py プロジェクト: lazerdaze/lancer
 def __init__(self):
     self.layerDataAvailable = None
     self.mll = MllInterface()
     self.clipboard = WeightsClipboard(self.mll)
コード例 #42
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)
コード例 #43
0
ファイル: ng_extra_tools.py プロジェクト: AngryBaer/abMaya
class UseNgBrush(object):
    """ setup for custom brush operations, a new instance is created on every brush stroke """
    def __init__(self, surface_name):
        self.surface = surface_name
        self.stroke_id = None
        self.mode = 1
        self.value = 1
        self.volumeThreshold = -0.1

        self.mll = MllInterface()
        self.selection_name = cmds.ls(selection=True)
        self.mesh = cmds.listRelatives(self.selection_name[0], shapes=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.max_value = max(self.ngs_weight_list)
        self.min_value = min(self.ngs_weight_list)

    def stroke_initialize(self):
        """ this function is executed before each brush stroke """
        cmds.undoInfo(openChunk=True, undoName="paint stroke")
        get_stroke_id = ngLayerPaintCtxInitialize(self.mesh[0])
        self.stroke_id = int(get_stroke_id.split(" ")[1])
        cmds.ngSkinLayer(paintOperation=self.mode, paintIntensity=self.value)
        self.stroke_update()
        return self.surface, self.stroke_id

    def stroke_finalize(self):
        """ this function is executed after each brush stroke """
        if self.stroke_id:
            cmds.ngLayerPaintCtxFinalize(self.stroke_id)
        self.stroke_id = None
        cmds.undoInfo(closeChunk=True)

    def stroke_update(self):
        """ updates certain attributes for the brush instance """
        # self.mll = MllInterface()
        # self.selection_name = cmds.ls(selection=True)
        # self.mesh = cmds.listRelatives(self.selection_name[0], shapes=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.max_value = max(self.ngs_weight_list)
        # self.min_value = min(self.ngs_weight_list)
        self.volumeThreshold = cmds.floatSlider("volume_slider",
                                                q=True,
                                                value=True)

    def contrast_paint(self, vert_id, value):
        """ sharpens the edge of the active weight map """
        vertex_weight = self.ngs_weight_list[vert_id]
        if not self.max_value > vertex_weight > self.min_value:
            return
        avg_value = (self.max_value + self.min_value) / 2
        normalized_range = self.max_value - self.min_value
        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 * value)
        contrast_weight = max(self.min_value,
                              min(contrast_weight, self.max_value))
        cmds.ngSkinLayer(paintIntensity=contrast_weight)
        cmds.ngLayerPaintCtxSetValue(self.stroke_id, vert_id, 1)

    def conceal_paint(self, vert_id, value):
        """ smooth operation that only lowers weight values """
        vertex_weight = self.ngs_weight_list[vert_id]
        if vertex_weight <= self.min_value:
            return
        vertex_name = '{}.vtx[{}]'.format(self.selection_name[0], vert_id)
        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)
        threshold = 0.01 / (value / 0.1)
        if weight_avg >= self.max_value and abs(weight_avg -
                                                vertex_weight) <= threshold:
            return
        weight_diff = abs(weight_avg - vertex_weight)
        conceal_weight = vertex_weight * (1 - (weight_diff * value))
        conceal_weight = max(conceal_weight, min_avg)
        cmds.ngSkinLayer(paintIntensity=conceal_weight)
        cmds.ngLayerPaintCtxSetValue(self.stroke_id, vert_id, 1)

    def spread_paint(self, vert_id, value):
        """ smooth operation that only increases weight values """
        vertex_weight = self.ngs_weight_list[vert_id]
        if vertex_weight >= self.max_value:
            return
        vertex_name = '.'.join([self.selection_name[0], 'vtx[%d]' % vert_id])
        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)
        threshold = 0.01 / (value / 0.1)
        if weight_avg <= self.min_value and abs(weight_avg -
                                                vertex_weight) <= threshold:
            return
        weight_diff = abs(weight_avg - vertex_weight)
        spread_weight = vertex_weight + (weight_diff * value)
        spread_weight = min(spread_weight, max_avg)
        cmds.ngSkinLayer(paintIntensity=spread_weight)
        cmds.ngLayerPaintCtxSetValue(self.stroke_id, vert_id, 1)

    def gain_paint(self, vert_id, value):
        """ increases existing weight values but preserves empty weights """
        vertex_weight = self.ngs_weight_list[vert_id]
        if vertex_weight == 0:
            return
        gain_weight = vertex_weight + (vertex_weight * value)
        gain_weight = min(gain_weight, 1)
        cmds.ngSkinLayer(paintIntensity=gain_weight)
        cmds.ngLayerPaintCtxSetValue(self.stroke_id, vert_id, 1)

    def volume_equalize(self, vert_id, value):
        """
        i.e a volumetric match operation with a falloff.
        applies weight values inside the brush radius onto other vertices inside a spherical volume
        """
        origin_vertex = '.'.join([self.selection_name[0], 'vtx[%s]']) % vert_id
        vertex_weight = self.ngs_weight_list[vert_id]
        v1 = cmds.pointPosition(origin_vertex)
        for i in range(len(self.ngs_weight_list)):
            target_weight = self.ngs_weight_list[i]
            if target_weight == vertex_weight:
                continue
            if i == vert_id:
                continue
            target_vertex = '.'.join([self.selection_name[0], 'vtx[%s]']) % i
            v2 = cmds.pointPosition(target_vertex)
            target_distance = sqrt((pow((v1[0] - v2[0]), 2)) +
                                   (pow((v1[1] - v2[1]), 2)) +
                                   (pow((v1[2] - v2[2]), 2)))
            if target_distance > (self.volumeThreshold * value):
                continue
            falloff = (self.volumeThreshold -
                       target_distance) / self.volumeThreshold
            eq_weight = target_weight - ((
                (target_weight - vertex_weight) * value) * falloff)
            cmds.ngSkinLayer(paintIntensity=eq_weight)
            cmds.ngLayerPaintCtxSetValue(self.stroke_id, i, 1)
コード例 #44
0
class VariousImportScenarios(AdditionalAsserts, unittest.TestCase):
    
    def setUp(self):
        self.targetMesh = 'testMesh' 
        self.numVerts = 169 # number of verts in our mesh
        self.mll = MllInterface()
        
        unittest.TestCase.setUp(self)
        openMayaFile('simplemirror.ma')
        cmds.hide("|y_axis")
        cmds.showHidden("|x_axis")
    
        self.mll.setCurrentMesh(self.targetMesh)
        self.mll.initLayers()
        self.mll.createLayer("original weights")
        
        self.model = ie.LayerData()
        self.layer = ie.Layer()
        self.layer.opacity = 1.0
        self.layer.enabled = True
        self.layer.mask = [0.0]*self.numVerts
        self.layer.mask[1] = 0.9 
        self.layer.mask[5] = 0.5566 
        self.layer.name = "imported layer"
        self.model.addLayer(self.layer)

        self.infl = ie.Influence()
        self.layer.addInfluence(self.infl)
        self.infl.influenceName = "x_axis|root|R_joint1"
        self.infl.logicalIndex = 666 # use nonsense value
        self.infl.weights = [0.1]*self.numVerts
        self.infl.weights[3] = 0.688
        self.infl.weights[4] = 0.345
        
        
    @decorators.insideMayaOnly
    def testSave(self):
        self.model.saveTo(self.targetMesh)
        
        layers = list(self.mll.listLayers())
        id,name = layers[0]
        self.assertEquals("imported layer", name)
        self.assertFloatArraysEqual(self.mll.getLayerMask(id), self.layer.mask)
        self.assertFloatArraysEqual(self.mll.getInfluenceWeights(id, self.infl.logicalIndex), self.infl.weights)
        
    @decorators.insideMayaOnly
    def testEmptyMask(self):
        self.layer.mask = []
        self.model.saveTo(self.targetMesh)
        

    @decorators.insideMayaOnly
    def testInvalidVertCount(self):
        self.infl.weights = self.infl.weights[:-1]
        with self.assertRaises(Exception,"Invalid weights count for influence 'x_axis|root|R_joint1' in layer 'imported layer': expected size is 169"):
            self.model.saveTo(self.targetMesh)
            
    @decorators.insideMayaOnly
    def testInvalidMaskCount(self):
        self.layer.mask = self.layer.mask[:-1]
        with self.assertRaises(Exception,"Invalid vertex count for mask in layer 'imported layer': expected size is 169"):
            self.model.saveTo(self.targetMesh)

    @decorators.insideMayaOnly
    def testInvalidMesh(self):
        with self.assertRaises(Exception,"could not initialize layers"):
            self.model.saveTo("persp")

    @decorators.insideMayaOnly
    def testUninitializedLayers(self):
        '''
        if layers on mesh were uninitialized, just initialize layers as usual, and do not fail
        '''
        LayerUtils.deleteCustomNodes()
        
        self.model.saveTo(self.targetMesh)

    @decorators.insideMayaOnly
    def testMissingInfluence(self):
        self.infl.influenceName = "something_is_missing_here"
        
        with self.assertRaises(MessageException,"Could not find influence 'something_is_missing_here' in skinCluster1"):
            self.model.saveTo(self.targetMesh)
コード例 #45
0
ファイル: layerDataModel.py プロジェクト: lazerdaze/lancer
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
コード例 #46
0
ファイル: initTransferWindow.py プロジェクト: leandropim/Tapp
 def ensureTargetMeshLayers(self):
     targetMll = MllInterface(mesh=self.targetMesh)
     if not targetMll.getLayersAvailable():
         targetMll.initLayers()
コード例 #47
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)
コード例 #48
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)
コード例 #49
0
class DuplicateLayersTest(AdditionalAsserts, unittest.TestCase):
    def setUp(self):
        unittest.TestCase.setUp(self)
        
        self.mll = MllInterface()
        
        self.setup = DuplicateLayers()
        self.setup.setMllInterface(self.mll)
        
        testUtils.openMayaFile("influence transfer.mb")
        
        self.mll.setCurrentMesh('sourceMesh')
        
        self.mll.initLayers()
        
    def testLayerName(self):
        self.assertEquals("layer1 copy",self.setup.createLayerName("layer1"))
        self.assertEquals("layer1 copy(2)",self.setup.createLayerName("layer1 copy"))
        self.assertEquals("layer1 copy(1000001)",self.setup.createLayerName("layer1 copy(1000000)"))
        
        
    @insideMayaOnly
    def testDuplicateOneLayer(self):
        id = self.mll.createLayer("layer1 copy")
        self.setup.addLayer(id)
        self.setup.execute()
        
        newId = self.setup.duplicateIds[0]
        self.assertEqual(self.mll.getLayerName(newId),'layer1 copy(2)')
        self.assertEqual(self.mll.isLayerEnabled(newId),True)
        self.assertAlmostEqual(self.mll.getLayerOpacity(newId),1.0)
        self.assertFloatArraysEqual(self.mll.getLayerMask(newId), [])
        
        self.assertFloatArraysEqual(self.mll.getInfluenceWeights(id, 1), self.mll.getInfluenceWeights(newId, 1))
    
    @insideMayaOnly
    def testLayerOrder(self):
        id1 = self.mll.createLayer("layer1")
        id2 = self.mll.createLayer("layer2")
        
        def layerNames():
            return [a[1] for a in self.mll.listLayers()]
    
        self.assertArraysEqual(layerNames(), ["layer2","layer1"])
        
        self.setup.addLayer(id1)
        self.setup.addLayer(id2)
        
        self.setup.execute()
        
        # order respects "newer layers on top"
        self.assertArraysEqual(layerNames(), ["layer2 copy","layer1 copy","layer2","layer1"])
        
        # individual indexes in copy array respect indexes in the original array
        self.assertEquals(self.mll.getLayerName(self.setup.duplicateIds[0]),"layer1 copy")
        self.assertEquals(self.mll.getLayerName(self.setup.duplicateIds[1]),"layer2 copy")
コード例 #50
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)