Detect Hirarchy Change in a tag
-
ok, well. I guess i have to find another way.
is there a way for a tag plugin to notice, that the object it is on has changed within the hierarchy? like the MSG_CHANGE? That could help me
-
Hi @cgweasel I forked your topic, in the future please create a new topic each time you have a question with a topic that is unrelated to the current discussion.
With that said there is no 100% reliable way to do it, the best way would be to store the neighbor hierarchy (GetUp,GetPred,GetNext) and then add an execution step as Initial so within the Execute method you can check for these objects.
Find bellow and example adapted from py-look-at-camera example. The important methods to check are:
__init__
to initialize the cache valueAddToExecution
to add the Initial state executionisHierarchyChanged
to check if the cache changedupdateCachedHierarchy
to update the cacheExecute
withpriority == c4d.EXECUTIONPRIORITY_INITIAL
that will check the cache, and print a message when the hierarchy changed.
import os import c4d # Be sure to use a unique ID obtained from www.plugincafe.com PLUGIN_ID = 1028284 class LookAtCamera(c4d.plugins.TagData): """Look at Camera""" def __init__(self): self._up = None self._pred = None self._next = None def Init(self, node): """Called when Cinema 4D Initialize the TagData (used to define, default values). Args: node (c4d.GeListNode): The instance of the TagData. Returns: True on success, otherwise False. """ self.InitAttr(node, bool, c4d.PYLOOKATCAMERA_PITCH) node[c4d.PYLOOKATCAMERA_PITCH] = True pd = c4d.PriorityData() if pd is None: raise MemoryError("Failed to create a priority data.") pd.SetPriorityValue(c4d.PRIORITYVALUE_CAMERADEPENDENT, True) node[c4d.EXPRESSION_PRIORITY] = pd return True def AddToExecution(self, tag, prioList): """Called By Cinema 4D to determine when in the Execution Pipeline the Execute method of this tag should be called. Args: tag (c4d.BaseTag): The instance of the TagData. prioList (c4d.plugins.PriorityList): The priority list to add your tag’s execution points to. Returns: True if the hierarchy changed, otherwise False. """ # Retrieve the user defined priority storedPriority = tag[c4d.EXPRESSION_PRIORITY] storedPriorityValue = storedPriority.GetPriorityValue(c4d.PRIORITYVALUE_MODE) + \ storedPriority.GetPriorityValue(c4d.PRIORITYVALUE_PRIORITY) # Add an execution during the Initial phase, this is when we will check for hierarchy change prioList.Add(tag, c4d.EXECUTIONPRIORITY_INITIAL, 0) # Add the user defined execution phase, therefor the Execute method will be called 2 times. prioList.Add(tag, storedPriorityValue, 0) return True def isHierarchyChanged(self, obj): """Check if any of the cached neighbor hierarchy is different from the actual one. Args: obj (c4d.BaseObject): The host object to compare the hierarchy from. Returns: True if the hierarchy changed, otherwise False. """ up = obj.GetUp() if up != self._up: return True pred = obj.GetPred() if pred != self._pred: return True next = obj.GetNext() if next != self._next: return True return False def updateCachedHierarchy(self, obj): """Update the cached neighbor hierarchy. Args: obj (c4d.BaseObject): The host object to retrieve the hierarchy from. """ self._up = obj.GetUp() self._pred = obj.GetPred() self._next = obj.GetNext() def Execute(self, tag, doc, op, bt, priority, flags): """Called by Cinema 4D at each Scene Execution, this is the place where calculation should take place. Args: tag (c4d.BaseTag): The instance of the TagData. doc (c4d.documents.BaseDocument): The host document of the tag's object. op (c4d.BaseObject): The host object of the tag. bt (c4d.threading.BaseThread): The Thread that execute the this TagData. priority (EXECUTIONPRIORITY): Information about the execution priority of this TagData. flags (EXECUTIONFLAGS): Information about when this TagData is executed. """ if priority == c4d.EXECUTIONPRIORITY_INITIAL: if self.isHierarchyChanged(op): self.updateCachedHierarchy(op) print("Hierarchy Changed") # We don't want to execute the logic of our tag during this phase except if the user asked for it storedPriority = tag[c4d.EXPRESSION_PRIORITY] storedPriorityValue = storedPriority.GetPriorityValue(c4d.PRIORITYVALUE_MODE) + \ storedPriority.GetPriorityValue(c4d.PRIORITYVALUE_PRIORITY) if storedPriorityValue != c4d.EXECUTIONPRIORITY_INITIAL: return c4d.EXECUTIONRESULT_OK # Retrieves the current active base draw bd = doc.GetRenderBaseDraw() if bd is None: return c4d.EXECUTIONRESULT_OK # Retrieves the active camera cp = bd.GetSceneCamera(doc) if bd.GetSceneCamera(doc) is not None else bd.GetEditorCamera() if cp is None: return c4d.EXECUTIONRESULT_OK # Calculates the position to target local = cp.GetMg().off * (~(op.GetUpMg() * op.GetFrozenMln())) - op.GetRelPos() # Calculates the rotation to target hpb = c4d.utils.VectorToHPB(local) if not tag[c4d.PYLOOKATCAMERA_PITCH]: hpb.y = op.GetRelRot().y hpb.z = op.GetRelRot().z # Defines the rotation op.SetRelRot(hpb) return c4d.EXECUTIONRESULT_OK if __name__ == "__main__": # Retrieves the icon path directory, _ = os.path.split(__file__) fn = os.path.join(directory, "res", "tpylookatcamera.tif") # Creates a BaseBitmap bmp = c4d.bitmaps.BaseBitmap() if bmp is None: raise MemoryError("Failed to create a BaseBitmap.") # Init the BaseBitmap with the icon if bmp.InitWith(fn)[0] != c4d.IMAGERESULT_OK: raise MemoryError("Failed to initialize the BaseBitmap.") c4d.plugins.RegisterTagPlugin(id=PLUGIN_ID, str="Py - LookAtCamera", info=c4d.TAG_EXPRESSION | c4d.TAG_VISIBLE, g=LookAtCamera, description="Tpylookatcamera", icon=bmp)
Side note, we will be away the 26th and 27th December, so don't be surprise by the delay if you have follow-up questions, for more information see No support on 26/12 and 27/12.
Cheers,
Maxime. -
Hello @cgweasel,
without further questions or postings, we will consider this topic as solved by Friday 02/06/2023 and flag it accordingly.
Thank you for your understanding,
Maxime.