def Message - What Message is triggered when an Object is created or moved in the hierarchy.



  • I have a Python plugin that creates a ObjectData node that I use as a container for other objects by making them a child of this Custom object.

    Now some of the objects that are children of the Custom node that I made need to have some tags assigned to them automatically if they are parented to this object.

    For example if a user creates a Camera and puts it under my Custom Object I want to first check what kind of object it was that they put in, in this case a camera and then check that camera for the required tags. If they don't exist then create them.

    Ive gone over and over https://developers.maxon.net/docs/Cinema4DCPPSDK/html/group___m_s_g.html#ga3ef2f9d6ed136cb6fe4f22ed0381c380 Trying to find the correct message flags but am struggling.



  • Hi there,

    don't know much about ObjectData, but Python(*) docs read like you have to listen for MSG_DOCUMENTINFO and then retrieve the MSG_DOCUMENTINFO_TYPE_OBJECT_INSERT message. Then you can act accordingly. :)

    (*) You pointed to the C++ docs, but tagged your post with python.



  • Yeah most of the message codes are the same (that I have seen so far)

    But dope thank you that is prolly what I am looking for!

    So basically on that message check what object it is being inserted and then check if its master parent is my container object then run my updates, thank you so much!

    UPDATE This got me going the right direction actually but I can't get the message to actually fire anytime I insert an object.

        def Message(self, node, type, data):
        
            if type ==  c4d.MSG_DESCRIPTION_COMMAND:
                if data['id'][0].id == BJS_EXPORT_SCENE_TEMPLATE:
                    self.Export(node)
            
            if type == c4d.MSG_DOCUMENTINFO_TYPE_OBJECT_INSERT:       
                print "OBJECT INSERTED"       
            
            return True    
    

    No matter what I insert I never see the message. If I change it to Documentinfo it fires if I save etc the document so at least that is working.

    but if I actually print out all the data from every message the only things that pop up on a Object Insert is

    {'h': 32, 'bmp': <c4d.bitmaps.BaseBitmap object at 0x00000214FEC3AF50>, 'flags': 0, 'w': 32, 'y': 0, 'x': 0, 'filled': False}
    {'res': None}
    {'res': None}
    

    Which does seem like Im doing this correctly then.



  • Hi, @Pryme8 there is no such a message sent to the object (Since the behavior to add tag according to the parent does not really exist within Cinema 4D).

    So the best way to do that is at the start of the Scene Execution. But the issue is that during the scene Execution since you are in a threaded environment you are not allowed to modify the scene.

    So the correct way is to add your execution state with AddToExecution then in your Execute method, send a core message with SpecialEventAdd.
    Then in a MessageData.CoreMessage (that will be processed in the main thread) you are free to modify the scene, so you can add your tag.

    Here a quick example I will update it later to clean it a bit, but here are the idea

    import c4d
    
    # Be sure to use a unique ID obtained from www.plugincafe.com
    PLUGIN_ID = 2345678
    
    # This is a global list that will be used to store the camera that needs a tag.
    globalList = []
    
    class ObjectDataAddTag(c4d.plugins.ObjectData):
        def __init__(self, *args):
            super(ObjectDataAddTag, self).__init__(*args)
            self.SetOptimizeCache(True)
    
        def GetVirtualObjects(self, op, hierarchyhelp):
            return None
    
        def HierarchyIterator(self, obj):
            while obj:
                yield obj
                for opChild in self.HierarchyIterator(obj.GetDown()):
                    yield opChild
                obj = obj.GetNext()
    
        def Execute(self, op, doc, bt, priority, flags):
            # Loops over all children object
            for obj in self.HierarchyIterator(op.GetDown()):
                # if its not a camera do nothing
                if not obj.CheckType(c4d.Ocamera):
                    continue
    
                # Retrieve if there is already a tag ( here we try the Tphong)
                tag = obj.GetTag(c4d.Tphong)
                if tag is not None:
                    continue
    
                # Acquire the global lock, so we are sure nothing else will change the content of globalList
                c4d.threading.GeThreadLock()
    
                # Adds the Camera object to our globalList
                globalList.append(obj)
    
                # Release the global lock, so other parts can do stuff on the globalList
                c4d.threading.GeThreadUnlock()
    
                # Add a CoreMessage on the CoreMessage Queue, this will be processed later (as soon as the main thread is free)
                # This ID should be unique and it will be the one that will be listening for in the MessageData.CoreMessage method.
                c4d.SpecialEventAdd(PLUGIN_ID)
    
            return c4d.EXECUTIONRESULT_OK
    
        def AddToExecution(self, op, priorityList):
            # This will call the Execute method during the initial phase of the scene execution
            priorityList.Add(op, c4d.EXECUTIONPRIORITY_INITIAL, c4d.EXECUTIONFLAGS_NONE)
            return True
    
    class TagCreateMessageData(c4d.plugins.MessageData):
    
        def CoreMessage(self, id, bc):
            if id == PLUGIN_ID:
                # Avoid unnecessary threading lock if there is nothing stored
                # Since we have no idea how Core Message is consumed, it can happen that 3/4 message will be executed on a raw
                # But since we consume all the stacked even the first time.
                if not len(globalList):
                    return
    
                # Stops all other threads that may read the scene.
                c4d.StopAllThreads()
    
                # Acquire the global lock, so we are sure nothing else will change the content of globalList
                c4d.threading.GeThreadLock()
    
                # Iterates over all stored camera object in globalList and remove them (aka act as a queue)
                while len(globalList):
                    # Retrieves the camera object, its important to also test if the object is still alive.
                    # A CoreMessage execution may happen way later (few ms) than when it was triggered, so during this time 
                    # maybe other parts destructed the C++ object the Python object is pointing into.
                    cameraObj = globalList.pop()
                    if cameraObj is None and not cameraObj.IsAlive():
                        continue
    
                    # Creates a tag
                    cameraObj.MakeTag(c4d.Tphong)
    
                    # Pushes an update event to Cinema 4D
                    c4d.EventAdd()
    
                # Release the global lock, so other parts can do stuff on the globalList
                c4d.threading.GeThreadUnlock()
    
            return True
    
    if __name__ == "__main__":
        # Registers the object plugin, very important to add the flag OBJECT_CALL_ADDEXECUTION so AddToExecution is called.
        c4d.plugins.RegisterObjectPlugin(id=PLUGIN_ID,
                                         str="Py-ObjectDataCreateTag",
                                         g=ObjectDataAddTag,
                                         description="",
                                         icon=None,
                                         info=c4d.OBJECT_GENERATOR | c4d.OBJECT_CALL_ADDEXECUTION)
    
        c4d.plugins.RegisterMessagePlugin(id=1234567, str="", info=0, dat=TagCreateMessageData())
    

    Cheers,
    Maxime



  • Thank you so much! This helps me a lot with the understanding of what was going on. I am digging back into my work on this today and will follow up with your advice.