Connect + Delete groups iteratively
-
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()
-
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 -
@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.
-
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. -
hello,
I'will consider this thread as solved tomorrow if you have nothing to add
Cheers,
Manuel