Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Register
    • Login

    Spline modeling commands via OBJECT_MODIFIER (Python)

    Cinema 4D SDK
    2
    5
    1.1k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • merkvilsonM
      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.

      alt text

      But if I attempt to make it a child of the desired spline, nothing changes.

      alt text

      Let me know if I'm doing something wrong.

      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by m_adam

        Hi @merkvilson, just a quick reminder I turned on your topic as a Question, Please See Q&A Functionnality.

        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[0]
        
        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!
        Cheers,
        Maxime.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        1 Reply Last reply Reply Quote 3
        • merkvilsonM
          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).

          • Download plugin
          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by m_adam

            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. 😄
            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            1 Reply Last reply Reply Quote 2
            • merkvilsonM
              merkvilson
              last edited by

              @m_adam said in Spline modeling commands via OBJECT_MODIFIER (Python):

              Maxime

              Thank you, Maxime for your explicit and informative answer! ♥
              I'll take a look into it.

              1 Reply Last reply Reply Quote 0
              • First post
                Last post