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

    External Compositing Tag not working with generated objects by ObjectData Plugin

    Cinema 4D SDK
    python
    2
    5
    816
    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.
    • E
      Eduardo Oliveira
      last edited by

      Hi All,
      I'm on my first plugin, and by no means a true Python developer.
      my plugin is relatively simple : it gets a cloner from a LINK box in the GUI, and creates a null for each clone, with a compositing tag attached, and gets the matrix of each clone from the GetCache() and applies it to each null, making them follow the clones.

      It all works fine on the viewport, but if i export an AEC file, the nulls are all static, and not following changes caused by effectors, like position or rotation etc. it DOES work on the viewport as you scrub the timeline in C4D though.

      i'm creating the Nulls during the GetVirtualObjects() function, and changing their Matrix on the Execute() function.

      Any ideas?

      Cheers,
      Eddie

      1 Reply Last reply Reply Quote 0
      • r_giganteR
        r_gigante
        last edited by r_gigante

        Hi Eddy, welcome for the Forum and thanks for reaching out us.

        With regard to your issue, I think the problem is how you handle your ObjectData cache and when if update it during the scene evaluation. In the code below beside checking for my object cache existence and for my object's parameters changes I also check if the cloner's cache has changed when the ObjectData::GetVirtualObjects is called.

        class PC_12764 (plugins.ObjectData):
        	_clonerDirty = 0
        
        	def Init(self, node):
        		return True
        
        	def GetVirtualObjects(self, op, hh):
        		cloner = op[1002]
        		res = c4d.BaseObject(c4d.Onull)
        		if cloner is None:
        			return res
        
        		clonerCache = cloner.GetCache()
        		if clonerCache is None:
        			return res
        
        		firstClonerItem = clonerCache.GetDown()
        		if firstClonerItem is None:
        			return res
        
        		# check cloner cache dirty count
        		clonerDirty = False
        		if self._clonerDirty != cloner.GetDirty(c4d.DIRTYFLAGS_CACHE):
        			clonerDirty = True
        			self._clonerDirty = cloner.GetDirty(c4d.DIRTYFLAGS_CACHE)
        		
        		# return object cache taking in account cloner cache
        		dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTY_DATA) or clonerDirty
        		if not dirty :
        			return op.GetCache(hh)
        		
        		while firstClonerItem is not None:
        			child = c4d.BaseObject(c4d.Onull)
        			child.SetMg(firstClonerItem.GetMg())
        			child.InsertUnder(res)
        			firstClonerItem = firstClonerItem.GetNext()
        
        		return res
        

        This approach, without using the ObjectData::Execute() gives ,in the end, consistent results when exporting a .aec file using a External Compositing Tag as in the scene attached.

        AEC file except

        ...
        ...
        
        NULL2 "Null"
        {
          NULLTYPE 1
          SIZEX 200
          SIZEY 100
          ANCHOR 3
          ORIENTATION 1
          COLOR 1 0 0 
          SHOWFROM 1
          SHOWTO 10
          KEY 1 200 200 200 0 0 0 0 0 0
          KEY 2 202.036 195.61 204.755 0 0 0 0 0 0
          KEY 3 206.929 185.058 216.184 0 0 0 0 0 0
          KEY 4 212.786 172.427 229.866 0 0 0 0 0 0
          KEY 5 217.679 161.875 241.295 0 0 0 0 0 0
          KEY 6 219.714 157.485 246.051 0 0 0 0 0 0
          KEY 7 217.679 161.875 241.295 0 0 0 0 0 0
          KEY 8 212.786 172.427 229.866 0 0 0 0 0 0
          KEY 9 206.929 185.058 216.184 0 0 0 0 0 0
          KEY 10 202.036 195.61 204.755 0 0 0 0 0 0
        }
        
        NULL2 "Null"
        {
          NULLTYPE 1
          SIZEX 200
          SIZEY 100
          ANCHOR 3
          ORIENTATION 1
          COLOR 1 0 0 
          SHOWFROM 1
          SHOWTO 10
          KEY 1 0 200 200 0 0 0 0 0 0
          KEY 2 0.427 201.541 196.343 0 0 0 0 0 0
          KEY 3 1.453 205.246 187.554 0 0 0 0 0 0
          KEY 4 2.681 209.681 177.032 0 0 0 0 0 0
          KEY 5 3.707 213.385 168.242 0 0 0 0 0 0
          KEY 6 4.134 214.927 164.585 0 0 0 0 0 0
          KEY 7 3.707 213.385 168.242 0 0 0 0 0 0
          KEY 8 2.681 209.681 177.032 0 0 0 0 0 0
          KEY 9 1.453 205.246 187.554 0 0 0 0 0 0
          KEY 10 0.427 201.541 196.343 0 0 0 0 0 0
        ...
        ...
        }
        

        Scene file

        PC_12764.c4d

        1 Reply Last reply Reply Quote 0
        • E
          Eduardo Oliveira
          last edited by

          Thanks for the reply - sorry to resurrect this post, but I am still struggling to put your advice to use. since I am mostly writing this plugin to learn, I'll go ahead and just paste the code in here ( please be gentle)

          class CloneStalker (plugins.ObjectData):
          def init (self):
          self.SetOptimizeCache(True)

          def Init(self, node):
              # Parameter initialization
              self.InitAttr(node, float, [c4d.CL_STALKER_MAX_NULLS])
          
              node[c4d.CL_STALKER_MAX_NULLS] = 100.0
          
          
              pd = c4d.PriorityData()
              if pd is None:
                  raise MemoryError("Failed to create a priority data.")
          
              pd.SetPriorityValue(c4d.PRIORITYVALUE_MODE, 4)
              pd.SetPriorityValue(c4d.PRIORITYVALUE_PRIORITY, 100)
              node[c4d.EXPRESSION_PRIORITY] = pd
          
          
              return True
          
          
          def CheckDirty(self, op, doc):
          
              frame = doc.GetTime().GetFrame(doc.GetFps())
              if frame != lastFrame:
                  lastFrame = frame
                  op.SetDirty(c4d.DIRTYFLAGS_DATA)
          
          def AddToExecution(self, op, list) :  
              list.Add(op, c4d.EXECUTIONPRIORITY_GENERATOR, 400)  
              return True  
          
          
          
          def GetVirtualObjects(self, op, hh):
              cloner = op[c4d.CL_STALKER_TARGET]
              doc = c4d.documents.GetActiveDocument()
          
              def GetStalkers(bname,tgt):
                  allChild = tgt.GetChildren()
                  stalkerChilds = []
                  if allChild != None:
                      for i in allChild:
                          childname = i[c4d.ID_BASELIST_NAME]
                          bnamelenght = len(bname)
                          subname = childname[0:bnamelenght]
                          if subname == bname:
                              stalkerChilds.append(i)
          
                  return stalkerChilds
          
              if cloner != None:
                  modata=mo.GeGetMoData(cloner)
                  if modata != None:
                      cache =  cloner.GetDeformCache()
                      if cache is None:
                          cache = cloner.GetCache()
                      clones = cache.GetChildren()
                      name = str(cloner[c4d.ID_BASELIST_NAME]) + ".Cloner Stalker"
                      child_basename = op[c4d.ID_BASELIST_NAME].replace(".Cloner Stalker", ".Stalker.")
          
                      if cloner.GetNext() != op:
                          op.InsertAfter(cloner)
                      null= c4d.BaseObject(c4d.Onull)
                      null[c4d.NULLOBJECT_DISPLAY]=0
                      null[c4d.ID_BASELIST_NAME] = name
                      parent=op
                      
          
                      md = mo.GeGetMoData(cloner)
                      marr = md.GetArray(c4d.MODATA_MATRIX)
                      count = md.GetCount()
                      Selection = []
                      maxNulls = op[c4d.CL_STALKER_MAX_NULLS]
                      lastChild = None
                      child = GetStalkers(child_basename,op)
                      if mo.GeGetMoDataSelection(cloner) == None:
                          for i in range(count):
                              Selection.append(i)
                          selected=Selection
                      if  mo.GeGetMoDataSelection(cloner)!= None:
                          Selection = mo.GeGetMoDataSelection(cloner).GetAll(count)
                          selected=[i for i,x in enumerate(Selection) if x==1]
          
                      if op[c4d.ID_BASELIST_NAME]!=name:
                          child_basename = op[c4d.ID_BASELIST_NAME].replace(".Cloner Stalker", ".Stalker.")
                          if len(child)>0:
                              for i in child:
                                  old_name =i[c4d.ID_BASELIST_NAME]
                                  index_name = old_name[len(child_basename):len(old_name)]
                                  n_name = cloner[c4d.ID_BASELIST_NAME] + ".Stalker." + index_name
                                  i[c4d.ID_BASELIST_NAME] = n_name
                                  print (n_name)
                          
                          op[c4d.ID_BASELIST_NAME] = name
                          child_basename = op[c4d.ID_BASELIST_NAME].replace(".Cloner Stalker", ".Stalker.")
                          child = GetStalkers(child_basename,op)    
                          parent=op
                      op.SetMg(cloner.GetMg())
          
                      if len(child)<len(selected) and len(child) < maxNulls:
                          currChilds=[]
                          for i in child:
                              child_name = i[c4d.ID_BASELIST_NAME]
                              nameLenght = len(child_name)
                              index = int(child_name[len(child_basename):nameLenght])
                              currChilds.append(index)
                          new = [item for item in selected if item not in currChilds]
                          for i in new:
                              obj= c4d.BaseObject(c4d.Onull)
                              obj[c4d.ID_BASELIST_NAME] = child_basename + str(i)
                              obj[c4d.NULLOBJECT_DISPLAY]=3
                              obj[c4d.NULLOBJECT_RADIUS]=50
                              extCompTag = c4d.BaseTag(465000402)
                              matrix = clones[i].GetMg()
                              obj.SetMg(matrix)
                              obj.InsertTag(extCompTag)
                              if len(child)>0:
                                  lastChild = child[(len(child)-1)]
                              doc.InsertObject(obj,parent,lastChild)
                              child = GetStalkers(child_basename,op)
                              if len(child)==maxNulls:
                                  break
                      if len(child)>len(selected) or len(child)>maxNulls:
                          for i in child:
                              child_name = i[c4d.ID_BASELIST_NAME]
                              nameLenght = len(child_name)
                              index = child_name[len(child_basename):nameLenght]
                              if not int(index) in selected:
                                  i.Remove()
                              child = GetStalkers(child_basename,op)
                          while len(child)>maxNulls:
                              lastChild = child[(len(child)-1)]
                              lastChild.Remove()
                              child = GetStalkers(child_basename,op)
                              
                     
                         
          def Execute(self, op, doc, bt, priority, flags) :
              
              def GetStalkers(bname,tgt):
                  allChild = tgt.GetChildren()
                  stalkerChilds = []
                  if allChild != None:
                      for i in allChild:
                          childname = i[c4d.ID_BASELIST_NAME]
                          bnamelenght = len(bname)
                          subname = childname[0:bnamelenght]
                          if subname == bname:
                              stalkerChilds.append(i)
          
                  return stalkerChilds
              cloner = op[c4d.CL_STALKER_TARGET]
              doc = c4d.documents.GetActiveDocument()
              name = str(cloner[c4d.ID_BASELIST_NAME]) + ".Cloner Stalker"
              child_basename = op[c4d.ID_BASELIST_NAME].replace(".Cloner Stalker", ".Stalker.")
              parent=op
              child = GetStalkers(child_basename,op)
              Selection = []
          
              if cloner != None:
                  modata=mo.GeGetMoData(cloner)
                  if modata != None:
                      cache =  cloner.GetDeformCache()
                      if cache is None:
                          cache = cloner.GetCache()
                      clones = cache.GetChildren()
                      md = mo.GeGetMoData(cloner)
                      marr = md.GetArray(c4d.MODATA_MATRIX)
                      count = md.GetCount()
          
                      if mo.GeGetMoDataSelection(cloner) == None:
                          for i in range(count):
                              Selection.append(i)
                              selected=Selection
                      if  mo.GeGetMoDataSelection(cloner)!= None:
                          Selection = mo.GeGetMoDataSelection(cloner).GetAll(count)
                          selected=[i for i,x in enumerate(Selection) if x==1]
          
                      for x in selected:
                          matrix = clones[x].GetMg()
                          ch_name = child_basename + str(x)
                          obj=c4d.documents.BaseDocument.SearchObject(doc,ch_name)
                          obj.SetMg(matrix)
                          print (clones[x].IsDirty(1))
              return c4d.EXECUTIONRESULT_OK        
          

          Please feel free to advise what i am doing wrong here, and what shuold i be doing instead.

          many thanks for your patience.
          cheers
          Eddie

          1 Reply Last reply Reply Quote 0
          • r_giganteR
            r_gigante
            last edited by

            Hi Eddie, thanks for getting back but I ask you to specify what is not working or is not clear in the answer marked as Solution.

            When posting code, I also recommend being sure that it's reduced to the bare minimum to reproduce the issue, that's properly documented and that a scene using it is attached to help us reproducing the issue rather than guessing what to do.

            Riccardo

            1 Reply Last reply Reply Quote 0
            • E
              Eduardo Oliveira
              last edited by Eduardo Oliveira

              ok, my apologies. i guess my question is at what point to i check for dirty, and at what point do i insert the nulls?

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