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