Does Maya API 2.0 have an equivalent undo class similar to PolyModifier.py?

Hey TA.org,

Has anyone implemented the polymodifier base class found here in API 2.0?

Basically, I have some node/command modeling tool(s) that need undo functionality after mesh editing. I should have stuck with api 1.0, but I just wanted to make sure, is this even possible in 2.0? Implementing a proper dag node insertion/deletion and mesh caching undo mechanism? ala polymodifier.py

To implement undo for mesh editing commands I basically subclass this implementation, inheriting from the base class and overriding the appropriate methods:

Overiding the methods in a similar way to this uv split tool example:

Works great, as long as all your code is all API 1.0. (I have memory corruption since my DoIt() code is api 2.0. It’s not too hard to convert, but yeah, would have been nice to use the 2.0 api.


Has anyone implemented polymodifier in API 2.0? All the autodesk examples don’t cover it…Seems like a big drawback to the API 2.0 if you cant leverage it in a similar fashion.

Just wanted to make sure before I write off the Python API 2.0 for these types of tools.

I tried converting the code in API 2.0, but we don’t have mutils, or the pointer handling, documentation is sparse so I’m giving up on that for now…It’s a bit over my head in terms of converting the pointers and such.

1 Like

I haven’t, I just wanted to say thanks for sharing that polyModifier.py, that’s a treasure trove of tips and tricks for working with undo/redo and the modifiers that seems to apply to both API 1.0 and 2.0!

Since the file is so large, am I understanding it correctly that there are certain commans in there not accessible via API 2.0? That would not surprise me. However, if you are looking for the equivalent means of implementing undo/redo for any arbitrary command, than API 2.0 does expose MPxCommand, which is where those things are written.

2 Likes

Hey marcuso,

Thanks for the reply :slight_smile: I did see your apiundo git, cmdx, and your google group posts regarding undo ha ha.

The part you mentioned, is what the uvspitcmd attempts via subclassing the polymodifier (which is built on top of the MPxCommand).

EDIT: polyModifier uses the MPxCommand you mentioned. It overrides the doit method, basically inserting a tweak node if tweaks exist on the current mesh, inserts your command as a node, wires it all up, etc. (It has like 4 main branches depending on history on/off etc).


Missing in API 2.0:

At least, to my understanding… I’m not sure we have this in API 2.0:
def __getFloat3PlugValue(self, plug):
See line 1355:

The use of xParam.asFloatPtr() as well as MscriptUtils (that’s a 1.0 obj right?) I’m not sure how to get that. Also, its one hell of a script to translate heh. I did translate uvsplitcmd and it works. (The other link in my original post).


If you see my other link, I just use polymodifier as a subclass in the same way as the splitUVcmd, and override specific methods they tell you to in the comments.

Similar to the uvsplitcmd and it works! (besides the memory corruption due to mixing apis). The uvsplit cmd (other link) was translated to api 2.0. It’s just the polyModifier.py meta class that I’m not sure about.


What PolyModifier handles:

How do you undo and pack data into a mesh with history? [The dag can erase your edits so a node is required].

How do you undo split/make holes/merge vertex?

Essentially its a mixture of inserting tweak nodes into the dag, as well as inserting your mesh modifications as a node. (but if the user has history disabled, baking them down into the mesh and restoring a cached mesh when the user Undos)

(You can read all about it in all the comments of polyModifier)

Polymodifier provides the ability (in the history disabled case) to restore a stashed mesh (it took me a day to digest all the branching cases that it handles, but they left quite good comments). <— You can’t just do this for history meshes, since dag evaluation will remove your changes.

That’s (and again to my understanding) why we need the Polymodifier meta class that autodesk provides in the documentation, we subclass it (see the other link in my original post for the splituv.cmd)


I’m pretty sure my script will work if I convert my doIt() function to API 1.0 code. It’s really just copying in your code to the splitUVcmd that autodesk provides. Just wish we could do it in 2.0.

I kind of asked if anyone has experience with this script because I find it a bit intimidating :smiley:

My tool is like 1k lines of code, this thing + undo/plugin boiler plate is like 2k lines more code haha.

But you know what… I might be able to convert it if this is the only thing:

 def __getFloat3PlugValue(self, plug):
                # Retrieve the value as an MObject
                object = plug.asMObject()

            # Convert the MObject to a float3
            numDataFn = OpenMaya.MFnNumericData(object)
            xParam = OpenMaya.MScriptUtil()
            xParam.createFromDouble(0.0)
            xPtr = xParam.asFloatPtr()
            yParam = OpenMaya.MScriptUtil()
            yParam.createFromDouble(0.0)
            yPtr = yParam.asFloatPtr()
            zParam = OpenMaya.MScriptUtil()
            zParam.createFromDouble(0.0)
            zPtr = zParam.asFloatPtr()
            numDataFn.getData3Float(xPtr, yPtr, zPtr)
            return OpenMaya.MFloatVector(
                                            OpenMaya.MScriptUtil(xPtr).asFloat(),
                                            OpenMaya.MScriptUtil(yPtr).asFloat(),
                                            OpenMaya.MScriptUtil(zPtr).asFloat())

I forgot how api 1.0 worked. I can ignore the mscript Utils…they are just to handle pointers like how you declare things in c++ I guess?
All this function does is return a float vector and lets you access the attributes?

Yes, that’s 1.0.

I’ve got a few conversion methods in cmdx here that might help.

The type of plug he’s working with looks like a compound, so you should be able to pass an API 2.0 MPlug of it into this function and it’ll get disambiguated into the 3 float components. See here how it just iterates over each of them individually?

What he’s returning there is the API 1.0 equivalent of a regular Python list of float values, e.g. [1.0, 2.0, 3.0] so if with 2.0 you really shouldn’t need to bother with it.

I would imagine it’s similar to handling matrices with a modifier.

Namely, you can pass any (?) type of data to an MPlug so long as you convert and pass it as an MObject. I haven’t tried this with meshes, but I would expect it to work.

Undo/redo is a hot topic over here at the moment, so I think I see where you’re coming from and where you’re going. The theory is simple enough; everything you create and modify should be uncreated and unmodified on undo. And you are the one responsible for keeping track of what those creations/modifications are. So there really aren’t any limitations on what can and cannot be undone/redone.

The MDagModifier/MDGModifier nodes can help a lot with that. If you manage to do everything using one or more of these modifiers, then all your undoIt needs to be is just calling their undoIt function. They can create/destroy/connect/disconnect/setAttribute/addAttribute/removeAttribute/almostEverything. And they’ll keep track of the order of those things for you.

My examples of using modifiers are somewhat obfuscated by them also being used via cmdx, but the pattern and how they are used is the same as regular API 2.0. Scan here for anything involving cmdx.DagModifier.

I would say that generally, working with undo is intimidating. :frowning: Sadly. It’s the one part we take for granted, until we have to implement it ourselves. I haven’t found a better method than to simply keep a close eye on exactly what it is you modify and explicitly reverse those steps yourself during undo.

One of the more damning issues with it, is that you sometimes want/need to intermingle maya.cmds or PyMEL amongst your command. But because they manage undo on their own, you can easily run into fatal crashes. E.g. if you create a command via maya.cmds but then modify it via your command. When you undo, you’ll have to make sure the cmds creation is undone first, otherwise you’ll fatal crash. (Edit: See how I confused the order here? It should be the opposite, of course. Happens much too easily even when trying to describe it!)

So it’s kind of an all-or-nothing ordeal. Either you do everything using API commands - outside of the maya.cmds undo management - or none of it. Mixing them is possible but you need to really know what you’re doing and the order in which they will get undone.

And don’t even get me started on redo moahaha :smiley:

2 Likes

Hmm yeah there are a few different ways. Let me digest :slight_smile:
PolyModifier + uvsplit handles all of it, redo, etc.
PM does mesh cache restore when history is disabled, otherwise it does tweak node insertion/your modifications as another node. (more or less).
If it just works, you can just plug in your code and be done with it haha.

I was hoping to just sneak my code in and hand wave it away.
But it looks I’ll need to study your links, and my original links more closely. Conversion might be possible still.

Much thanks for all the info!

To break things down, my impression is you’ve got two independent challenges.

  1. Does API 2.0 provide you with all that you need? It doesn’t expose as much as 1.0, so it’s not a given.
  2. Can it be undone/redone? Does it need to involve calling maya.cmds, and if so, can the order of undo/redo be tamed? E.g. could you temporarily disable undo during calls to cmds? Is it safe?

The “great” thing about undo/redo is that it doesn’t matter how you do it. There is only one Maya scene. If you create a node using API 1.0 and delete it with 2.0, that’s not an issue. They are simply two methods of manipulating the same data.

2 Likes

Yeah I’ll have to dig in more. All I need to do is move points and collapse edges which I’m doing. So I can side step the cmds calling, which is more complexity. What I like about polymodifier is slipping your code in after subclassing it is really simple actually, and it takes care of everything.

By the way check this out:
He uses the polyModifier class (in an API 1.0 plugin). It takes care of the undo/redo etc.
All my code does is move points, then collapse edges via the api… so hopefully PolyModifier is all I need. Either I get it to work with 2.0, or I write my function as 1.0 code.

Might be a useful link if you do mesh modification type plugins :slight_smile:

EDIT: woah just found this:
polyModifier also uses the mdgmodifier undoit (like you mentioned) in the case of node removal:
dgModifier.undoIt()

Yeah if I can get it to work with 2.0… it unifies all the undo paths (polyModifier), you can just use it for everything I think.

1 Like

Does anyone have experience writing modeling tools with the api? Please comment regarding undo and tweaks… it appears I cant really ignore them either since all tools account for tweaks…


I’m super confused regarding tweaks, do I code for them, are they handle internally?

So I managed to translate both the uvsplit tool and the polymodifier base class to API 2.0.

But after doing some testing of both that and these 1.0 examples that autodesk provides, neither do what they say they are suppose to do (based on the comments of both example files) without errors. Silly me, thinking a 12 year old example in the Maya 2020 docs would work.

Polymodifier based on the comments has all this handling of history/tweaks, all of which seem uncessary with maya today…

They pretty much ignore the user construction history toggle, and only insert a node if there is history present on the mesh (not what the comments say the code does), and tweak nodes only happen when there is a history string present, but the tweaks totally mess up the mesh.

It’s frustrating because I’m seeing this just with loading in the 1.0 plugin as is, so I know its not me.

Essentially, it probably doesnt matter that you are making mesh topology changes or editing point positions. The tweak node insertion is buggy.

My only hope is just a simpler implmentation of a simple node insertion via MDagModifier/MDGModifier.

I should be able to move points and collapse edges with a node, so undo / redo would simple be node insertion/deletion right?

Pre uvsplitcmd:

Post:
(notice the tweak node insertion since tweaks are present in history, I dont think this type of tweak node insertion is relevant to modern maya)
Undo/Redo works though

Yup, if I disable the tweak node the results look “correct”

So in short, I can ignore history settings, I can ignore tweaks, I can treat poly modification as just another deformer node, insert, undo = delete node, redo -> reinsert node…

Let me know if you have any thoughts on this

There is waaay too much to unpack here in order to give any sensible answer. :smiley: If you break the problem down to the smallest possible problem I’d be happy to try and help. For example, is the problem creating one node with MDagModifier, and then undoing it?

1 Like

It’s way to complicated.

Basically the polyModifier does not work as provided, it does not clean up tweaks properly.

Fixing the example requires tweak cleanup for both the undo and redo mechanisms.

I’m just going going to use cmds, I need to xform points and merge them for a modeling tool. Will try undo chunk…hopefully that works.