MArrays and undoing in Python API 2.0

Visto 96 veces
Saltar al primer mensaje no leído

dur

no leída,
9 oct 2018, 7:40:589/10/18
a Python Programming for Autodesk Maya
Hello,

So I have been quite stuck on a problem in regards to Maya crashing when I undo.
More specifically, it will crash on second, third or fourth undo. So the undo step *can* work.

def doIt(self, args):
self.parseArguments(args)
sel = om.MGlobal.getActiveSelectionList()
__, vertices = sel.getComponent(0)
fn_vertices = om.MFnSingleIndexedComponent(vertices)
self.verts = fn_vertices.getElements()
self.path = sel.getDagPath(0)
self.mesh = om.MFnMesh(self.path)
self.colors = self.mesh.getVertexColors()
self.sourceCols = self.mesh.getVertexColors()
self.redoIt()

def redoIt(self):
for v in self.verts:
self.colors[v].r = self.sourceCols[v].b
self.mesh.setVertexColor(self.colors[v], v)
def undoIt(self):
for v in self.verts:
self.mesh.setVertexColor(self.sourceCols[v], v)

This is the code. What I'm trying to do, is swap channels on a per vertex basis.
I only want it to act on selected vertices. I am explicitly copying the blue to the red channel for test purposes.

I know that some MArrays get referenced rather than copied, but I tried to check the sourceCols pre- and post redoIt, and it's identical.

My biggest hurdle here is that I don't really know what to look for? I'm really not certain what's happening behind the curtain, since to me it looks like pretty basic steps:

1. save original color
2. init redo and change colors and apply to mesh
3. apply original colors to mesh on undo

It always manages to get through the initial undo, but seems quite random after that.
I have been stuck on this thing for two days soon. Any help would really be appreciated!

Marcus Ottosson

no leída,
9 oct 2018, 13:23:439/10/18
a python_in...@googlegroups.com
Would it be possible to share a working example? Hard to say without testing it first-hand, nothing looks immediately off with the code so far. If anything, it looks a little nefarious that it's remembering `self.verts`, which'd contain references to `MObject`s that may or may not be valid at the time of undo. Might be worth trying with a `MObjectHandle` and checking it's `isAlive` prior to using it?

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/32ee3511-374b-4a10-9786-618e1de289fb%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

dur

no leída,
9 oct 2018, 14:37:329/10/18
a Python Programming for Autodesk Maya
Oh yeah of course. Here's the code as a cmd plugin.

import sys
import os
import json
import math
import maya.cmds as cmds
import maya.mel as mel
import maya.api.OpenMaya as om

inChFlagShort = '-fc'
inChFlagLong = '-fromChannel'
outChFlagShort = '-tc'
outChFlagLong = '-toChannel'
addChFlagShort = '-add'
addChFlagLong = '-addChannel'

def maya_useNewAPI():
    """
    The presence of this function tells Maya that the plugin produces, and
    expects to be passed, objects created using the Maya Python API 2.0.
    """
    pass

class CopyChannels(om.MPxCommand):
    kPluginCmdName = "copyChannels"

    def __init__(self):
        om.MPxCommand.__init__(self)
        self.channels = ''
        self.verts = []
        self.sourceCols = None
        #self.schema = {'r': 0,'g': 1,'b': 2,'a': 3}

    @staticmethod
    def cmdCreator():
        return CopyChannels()

    def isUndoable(self):
        return True

    def doIt(self, args):
        if self.parseArguments(args) is False:
            print("Problem with args")
            return
        self.parseArguments(args)
        self.extractChannelPairs()
        sel = om.MGlobal.getActiveSelectionList()
        if sel.isEmpty():
            return
        __, vertices = sel.getComponent(0)
        fn_vertices = om.MFnSingleIndexedComponent(vertices)
        self.verts = fn_vertices.getElements()
        self.path = sel.getDagPath(0)
        self.mesh = om.MFnMesh(self.path)
        self.vertexCount = self.mesh.numVertices
        self.colors = self.mesh.getVertexColors()
        self.sourceCols = self.mesh.getVertexColors()
        self.redoIt()


    def redoIt(self):
        for v in self.verts:
            self.colors[v].r = self.sourceCols[v].b
            self.mesh.setVertexColor(self.colors[v], v)

    def undoIt(self):
        for v in self.verts:
            self.mesh.setVertexColor(self.sourceCols[v], v)
        
    def parseArguments(self, args):

        argData = om.MArgParser(self.syntax(), args)

        if argData.isFlagSet(inChFlagShort):
            self.channels = argData.flagArgumentString(inChFlagShort, 0)
        else:
            print("Flag {} is missing".format(inChFlagLong))
            return False
        if argData.isFlagSet(addChFlagShort):
            self.addToChannel = argData.flagArgumentBool(addChFlagShort, 0)
        else:
            self.addToChannel = False
        return True

    def extractChannelPairs(self):
        self.channelPairs = self.channels.split('_')[1:]
        for ch in self.channelPairs:
            print(ch)



def syntaxCreator():
    syntax = om.MSyntax()

    syntax.useSelectionAsDefault(True)
    syntax.setObjectType(om.MSyntax.kSelectionList)
    
    syntax.addFlag(inChFlagShort, inChFlagLong, om.MSyntax.kString)
    #syntax.addFlag(outChFlagShort, outChFlagLong, om.MSyntax.kString)
    syntax.addFlag(addChFlagShort, addChFlagLong, om.MSyntax.kDouble)

    return syntax

commands = [
    CopyChannels
]

def initializePlugin(plugin):

    pluginFn = om.MFnPlugin(plugin)
    for cmd in commands:
        try:
            pluginFn.registerCommand(cmd.kPluginCmdName, cmd.cmdCreator, syntaxCreator)
        except:
            sys.stderr.write("Failed to register command: %s\n" % cmd.kPluginCmdName)
            raise


def uninitializePlugin(plugin):
    pluginFn = om.MFnPlugin(plugin)
    for cmd in commands:
        try:
            pluginFn.deregisterCommand(cmd.kPluginCmdName)
        except:
            sys.stderr.write("Failed to unregister command: %s\n" % cmd.kPluginCmdName )
            raise


By all means try it out. I'll have a look into the MObjectHandle as well. Thanks for your input :)
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

dur

no leída,
10 oct 2018, 3:53:1910/10/18
a Python Programming for Autodesk Maya
I am not entirely sure whether I'm using the MObjectHandle correctly, but I added 'self.vertsHandle = om.MObjectHandle(vertices)' to the doIt() function. Where 'vertices' is from the 'sel.getComponent(0)'. In the undoIt() function, I put the condition of 'self.vertsHandle.isAlive()'. It still manages to crash on undo. I added some print statements to figure out where it starts bugging. So in the undoIt, I'm printing the vert ID, the color at that ID (self.sourceCols[v]) as well as the mesh (Mem address). I am only printing, and not doing the assignment with setVertexColor, so like this:

def undoIt(self):
   if self.vertsHandle.isAlive():
       print(self.vertsHandle.isAlive())
       for v in self.verts:
           print("v is {}".format(v))
           print("sourceCols is {}".format(self.sourceCols[v]))
           print("Mesh is {}".format(self.mesh))

It manages to print correctly on all undos, so the references seem solid. And isAlive() returns true on it all. That kind of just leaves the mesh function setVertexColor I suppose? On Tech-artists.org Slack, Theodox mentioned there might be an issue somewhere with assignment of vertexColors and not faceVertexColors. My logic says if I use a getVertexColors, I have an array of MColors for each vertex, which shouldn't upset anything with faceVertexColors, but I've tried a bunch of things to get this working without luck, so my logic is failing me.

dur

no leída,
10 oct 2018, 9:32:1810/10/18
a Python Programming for Autodesk Maya
I won't directly say that I solved this, but I implemented it with using 'setColors' and 'assignColors', taking advatange of the 'colorset' system. This works.
I tried an implementation with faceVerts which also resulted in crashing. Where I assigned all faceVerts instead of individually. Using 'setColor' means only giving an MColorArray and optionally a colorset. So since you don't need an explicit reference to the verts, I would suspect the issue lies somewhere there.


On Tuesday, October 9, 2018 at 1:40:58 PM UTC+2, dur wrote:

mjlefevre

no leída,
10 oct 2018, 19:44:2410/10/18
a Python Programming for Autodesk Maya

Hi dur,

This works for me. I'm not sure, but it looks like a referencing issue as Marcus said.

From the docs: 

It is important to notice that the command does not store a pointer to an MObject, but rather uses an MDagPath to reference the curve for undo and redo. An MObject is not guaranteed to be valid the next time your command is executed. As a result, if you had used an MObject, Maya would likely core dump when performing your undoIt() or redoIt(). An MDagPath however, being simply a description of the path to the curve, is guaranteed to be correct whenever your command is executed. 


 
class CopyChannels(om.MPxCommand):
kPluginCmdName = "copyChannels"

def __init__(self):
om.MPxCommand.__init__(self)
self.channels = ''
self.verts = []
self.sourceCols = None
self.sel = None
self.path = om.MDagPath()
#self.schema = {'r': 0,'g': 1,'b': 2,'a': 3}

@staticmethod
def cmdCreator():
return CopyChannels()

def isUndoable(self):
return True

def doIt(self, args):
if self.parseArguments(args) is False:
print("Problem with args")
return
self.sel = om.MGlobal.getActiveSelectionList()
if self.sel.isEmpty():
return
self.parseArguments(args)
self.extractChannelPairs()
self.path = self.sel.getDagPath(0)
__, vertices = self.sel.getComponent(0)
fn_vertices = om.MFnSingleIndexedComponent(vertices)
self.verts = fn_vertices.getElements()
self.redoIt()

def redoIt(self):

self.mesh = om.MFnMesh(self.path)
self.vertexCount = self.mesh.numVertices
self.colors = self.mesh.getVertexColors()
self.sourceCols = self.mesh.getVertexColors()
for v in self.verts:
self.colors[v].r = self.sourceCols[v].b
self.mesh.setVertexColor(self.colors[v], v)

def undoIt(self):
self.mesh = om.MFnMesh(self.path)
for v in self.verts:
self.mesh.setVertexColor(self.sourceCols[v], v)

dur

no leída,
8 nov 2018, 7:41:118/11/18
a Python Programming for Autodesk Maya
Hey! Sorry I didn't notice the response, I ended up doing it by essentially using the setcolors, and setting the colorset, but this is really a great answer! And much less code.
Responder a todos
Responder al autor
Reenviar
0 mensajes nuevos