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
    • Login

    Connect + Delete groups iteratively

    Cinema 4D SDK
    python r20
    2
    5
    1.3k
    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.
    • esanE
      esan
      last edited by esan

      C4D R20

      I started the "SendModelCommand" path but trying to just get this stood up quickly with CallCommands first. The issue im having is obviously Connect + Delete happens on "All" selected objects, collapsing into one, when I want each to collapse into their own respective objects . My thoughts are to find all "MoText" under op root, store in an array and run the[ select > make editable >] select children of new hierarchy > connect+delete (deselect all?) stepping through the whole sequence per item. before moving onto the next item since connect+delete doesn't care about hierarchy, at least in CallCommand form anyway.

      I've made a couple of attempts at executing that logic, but am fairly new to writing python for C4D so haven't had success yet. Hoping i can be shown how to achieve my logic or be told/shown there's a much better way to go about it altogether.

      Thanks in advance!

      import c4d
      from c4d import gui
      from c4d import utils
      
      def selchildren(obj,next): # Scan obj hierarchy and select children
      
          while obj and obj != next:
      
              doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL,obj)
      
              if obj.CheckType(1019268): #find MoText
                  obj.SetBit(c4d.BIT_ACTIVE)
      
              selchildren(obj.GetDown(),next)
      
              obj = obj.GetNext()
      
          c4d.EventAdd()
      
          return True
      
      
      
      def main():
      
      
          for obj in doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN):
              if selchildren(obj, obj.GetNext()):
                  obj.DelBit(c4d.BIT_ACTIVE)
                  c4d.CallCommand(12236) # Make Editable
                  c4d.CallCommand(16768, 16768) # Connect + Delte
                  
      
      # Execute main()
      if __name__=='__main__':
          main()
      
      1 Reply Last reply Reply Quote 0
      • ManuelM
        Manuel
        last edited by Manuel

        hello,

        if i understood correctly, you want to make editable the motext, collapse the result (connect) and delete the original one.

        I did create an example for that.

        I've build the logic slowly. It was pretty straight forward.

        The first thing is to have a function that can return the next object in the hierarchy but that only act on the child and not the whole document.

        • For each selected object, i check in all children if i found a MoText.
        • Than, i send a modeling command to retrieve the current state, and a join command. (using SendModelingCommand)
        • I insert the newObject, using InsertObject, in the document (after the motext object) and i add the motext to a remove list.
        • once everything is done, i check the keyboard, if shift is pressed i don't remove the original motext.
        import c4d
        from c4d import gui
        
        def GetNextObject(ori, op):
            """ Retrieves the next object in the hierarchy. 
        
            :param      ori: the original object to sompare with the next so we never get upper this level.
            :type:     BaseObject
            :param      op: the actual object in the hierarchy
            :type:     BaseObject
            :Return:    the next object in the hierarchy or None
            :rtype:    BaseObject
            """
            if op==None:
                return None
            if op.GetDown():
                return op.GetDown()
        
            while not op.GetNext() and op.GetUp():
                # CHecks if the next object up in the hierarchy is not the original one.
                if op.GetUp() != ori:
                    op = op.GetUp()
                else:
                    return None
            return op.GetNext()
        
        
        def CSTO(op ):
            """ Current State To Object
            :param      op: the object to apply current state to object to.
            :type: BaseObject
            :return: the result of the command or raise an error if command failed
            :rtype: BaseObject
            """
            # Retrieves the document that the object belong to
            doc = op.GetDocument()
            # send the modeling command
            res = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_CURRENTSTATETOOBJECT,
                                        list = [op],
                                        mode = c4d.MODELINGCOMMANDMODE_ALL,
                                        bc = c4d.BaseContainer(),
                                        doc = doc)
            
            # Cheks if the command didn't failed
            if res is False:
                raise TypeError("return value of CSTO is not valie")
            
            # Returns the first object of the list.
            return res[0]
        
        def JoinCommand(op):
            """ Apply a join command to the object passe as argument
             :param      op: the object to apply current state to object to.
            :type: BaseObject
            :return: the result of the command or raise an error if command failed
            :rtype: BaseObject
            """
            # Retrieves the document that the object belong to
            doc = op.GetDocument()
        
            # send the modeling command
            res = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_JOIN,
                                        list = [op],
                                        doc= doc)
        
             # Cheks if the command didn't failed
            if res is False:
                raise TypeError("return value of Join command is not valie")
        
            # Returns the first object of the list.
            return res[0]
        
        
        # Main function
        def main():
            doc.StartUndo()
           
            # Retrieves the active objects
            objs = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE)
            if len(objs) <1:
                raise ValueError("select one objectr please")
            
            # prepare a list of object that need to be removed at the end.
            removeObjectList = []
        
            # Iterates trough the selected objects.
            for obj in objs:
                # Use another variable so we can keep one with the original object to compare in the 'GetNextObject' function
                currentObject = obj
                while currentObject:
                    # Checks if the current object is a MoText Object.
                    if currentObject.IsInstanceOf(1019268): 
                        myNewObject = CSTO(currentObject)
                        myNewObject = JoinCommand(myNewObject)
                        # Inserts the new object after the motext                
                        doc.InsertObject(myNewObject, None, currentObject)
                        doc.AddUndo(c4d.UNDOTYPE_NEW, myNewObject)
                        # Adds the motext object to the remove list
                        removeObjectList.append(currentObject)
        
                    currentObject = GetNextObject(obj, currentObject)
            
            # Retrieves the keyboard states for the shift key
            bc = c4d.BaseContainer()
            shift = None
            if c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD,c4d.BFM_INPUT_CHANNEL,bc):
                shift = bc[c4d.BFM_INPUT_QUALIFIER] & c4d.QSHIFT == c4d.QSHIFT
            
            # if shift key is not pressed, remove the objects.
            if not shift:
                # Removes all object in the remove list.
                for obj in removeObjectList:
                    doc.AddUndo(c4d.UNDOTYPE_DELETE, obj)
                    obj.Remove()
                
        
        
            doc.EndUndo()
            
            c4d.EventAdd()
        # Execute main()
        if __name__=='__main__':
            main()
        

        If you have any question just ask

        Cheers,
        Manuel

        MAXON SDK Specialist

        MAXON Registered Developer

        esanE 1 Reply Last reply Reply Quote 0
        • esanE
          esan @Manuel
          last edited by esan

          @m_magalhaes This is fantastic, it works and gives me a lot to dissect. I'll be further expanding on it to include Instance objects and cloners [have already had that working as theyre a simple "make editable"]. In my day to day i have to often share assets via FBX, and if my assets are built with instances/clonser/MoText [<-particularly messy], FBXs auto conversion of those assets create quite a messy hierarchy, roots on roots for days lol. This will help clean things up. Thanks!

          I think one issue i need to solve with this method is reapplying/maintaining the original objects applied Material.

          1 Reply Last reply Reply Quote 0
          • ManuelM
            Manuel
            last edited by

            hello,

            The code is using the same command that the user should use using the UI.
            Could you be a bit more specific about what's not working with material ?

            Cheers,
            Manuel.

            MAXON SDK Specialist

            MAXON Registered Developer

            1 Reply Last reply Reply Quote 0
            • ManuelM
              Manuel
              last edited by

              hello,

              I'will consider this thread as solved tomorrow if you have nothing to add 🙂

              Cheers,
              Manuel

              MAXON SDK Specialist

              MAXON Registered Developer

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