Spline modeling commands via OBJECT_MODIFIER (Python)
merkvilson last edited by merkvilson
Hello PluginCafe :)
I'm trying to affect Spline Objects via OBJECT_MODIFIER plugin but it has no influence when the modeling commands are taking place.
Let's assume I want to select an arbitrary number of points and delete them by utilizing MCOMMAND_DELETE
class Oresplines(plugins.ObjectData): def ModifyObject(self, mod, doc, op, op_mg, mod_mg, lod, flags, thread): op.GetPointS().SelectAll(5) res = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_DELETE, list = [op], mode = 1, doc = doc, flags = 0) return True
When I'm making the plugin a child of some polygonal object, it works as expected.
But if I attempt to make it a child of the desired spline, nothing changes.
Let me know if I'm doing something wrong.
Regarding your issue, C4D does not let you modify a Spline generator. As you may know, all spline generator hold a c4d.LineObject in their cache. And MCOMMAND_DELETE does fail on a LineObject, it only supports PolygonObject or SplineObject.
So you first need to convert your spline generator as a SplineObject.
import c4d def MakeEditableClone(obj): # We clone the obj, since MakeEditable, modify the passed object and we don't want that. oDoc = obj.GetClone() doc = c4d.documents.BaseDocument() doc.InsertObject(oDoc) res = c4d.utils.SendModelingCommand( command = c4d.MCOMMAND_MAKEEDITABLE, list = [obj], mode = c4d.MODELINGCOMMANDMODE_ALL, doc = doc) if not res: return False return res def Delete(obj): oDoc = obj.GetClone() if oDoc.GetDocument() is None: doc.InsertObject(oDoc) oDoc.GetPointS().SelectAll(1) res = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_DELETE, list = [oDoc], mode = 1, doc = doc, flags = 0) if res == True: retObj = oDoc.GetClone() oDoc.Remove() return retObj return False def main(): splineObj = MakeEditableClone(op) if not splineObj: return finalSpline = Delete(splineObj) if not finalSpline: return doc.InsertObject(finalSpline) c4d.EventAdd() # Execute main() if __name__=='__main__': main()
If you have any questions, please let me know!
merkvilson last edited by
Hello Maxime :)
Indeed, I know how to perform this operation in CommandData/Script but my question was about Object Modifier plugins (aka deformers).
Hi @merkvilson, to be honest, I completely overlooked the problem and thought op within ModifyObject would be a splineObject.
To clarify MCOMMAND_DELETE do not work on LineObject (which are the object for spline cache).
So in order to do so here a hacky solution.
Use the actual spline object from the scene, and then Apply the delete into this SplineObject (since there is no way to get a SplineObject from a LineObject).
class Oresplines(plugins.ObjectData): # Get obj and all its Children into a list def GetChildren(self, obj, next, modifiedObjs=None): if modifiedObjs is None: modifiedObjs = list() while obj and obj != next: modifiedObjs.append(obj) self.GetChildren(obj.GetDown(), next, modifiedObjs) obj = obj.GetNext() return modifiedObjs # Compare 2 line object and tell if it's the same one (based on the topology + space position of the object) def compareOline(self, a, b): if type(a) != type(b): return False if not a.CheckType(c4d.Oline): return False # Do different pass of checks from speedier to slower in order to leave as soon as possible if object does not match. # Fast check, check for pt count if a.GetPointCount() != b.GetPointCount(): return False # Fast check if a.GetMg() != b.GetMg(): return False # slower check if a.GetAllPoints() != b.GetAllPoints(): return False tagA = a.GetTag(c4d.Tline) tagB = b.GetTag(c4d.Tline) if not tagA or not tagB: return False # Even slower if tagA.GetAllHighlevelData() != tagB.GetAllHighlevelData(): return False return True def ModifyObject(self, mod, doc, op, op_mg, mod_mg, lod, flags, thread): # Handle polygons if not op.IsInstanceOf(c4d.Oline): op.GetPointS().SelectAll(5) res = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_DELETE, list = [op], mode = 1, doc = doc, flags = 0) return True # First step is to get all objects modified by our modifier. # Modifier works only if they are within another object. And all children of this hierarchy are modified. parent = mod.GetUp() isInGroup = bool(parent) # Should never happen if we are not in a group, nothing is modified, so ModifyObject is never called. if not isInGroup: return True # Get all children of the parent (aka all objects modified by our modifier). modifiedObjs = self.GetChildren(parent, mod.GetNext()) # Iterate over all modified objects, and check which LineObject is currently modified in the curent ModifyObject call. spline = None for obj in modifiedObjs: cache = obj.GetCache() if not cache: continue # Comparison of Oline might fail, C4D do not guarantee the execution order of ModifyObject. # So if we get A and B identical e.g. 2 c4d.Osplinecircle with same parameters, # ModifyObject may currently edit B before A, while the compare will return True to the first object matching the condition # So it may return true on A while B was modified. But in the end, it does not really matter since both will be modified just order might get swapped. if self.compareOline(cache, op): spline = obj break # Check if our modified object is a spline and nothing else if not spline or not spline.GetInfo()&c4d.OBJECT_ISSPLINE: return True # Then we get a copy of the SplineObject located in the scene splineObj = spline.GetRealSpline().GetClone() if not splineObj: return True # Create a tempo document for our delete operation workDoc = c4d.documents.BaseDocument() workDoc.InsertObject(splineObj) # Select and delet our points splineObj.GetPointS().SelectAll(1) res = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_DELETE, list = [splineObj], mode = c4d.MODELINGCOMMANDMODE_POINTSELECTION, doc = workDoc, flags = 0) if not res: return True # Build the cache, in order to generate a Oline object from the resulting SplineObject workDoc.ExecutePasses(thread, False, False, True, c4d.BUILDFLAGS_INTERNALRENDERER) # Get the Cache (Oline object) of our modified splineObject oLineModified = splineObj.GetCache() if not oLineModified: return True # Copy our modified LineObject to the op LineObject oLineModified.CopyTo(op, c4d.COPYFLAGS_NONE) return True
If you have any question, please let me know.
merkvilson last edited by
Thank you, Maxime for your explicit and informative answer!
I'll take a look into it.