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

    Inheritance created by Python script not getting saved

    Cinema 4D SDK
    python
    2
    8
    1.2k
    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.
    • J
      Joel
      last edited by

      Hi,
      I have a python script that creates inheritances with keyframes. All these work and get represented in the viewport, however when I save the project, close Cinema 4D, and then reopen the project, the inheritances are there with the keyframes, but their effects are not represented.

      And this only happens to the inheritances created by the script, manually created inheritances do work.

      What could be the issue?

      Thank you for your time.
      Joel.

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @Joel
        last edited by

        Hello @joel,

        Thank you for reaching out to us. Without your code and an example scene, it will be impossible to help you here. I am also not quite sure what you mean by 'creates inheritances with keyframes'. Are you talking about the 'Instance Object' and creating keyframes for it?

        Please share your code and an example scene file.

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        J 2 Replies Last reply Reply Quote 0
        • J
          Joel @ferdinand
          last edited by

          @ferdinand
          This is the part of the code corresponding to the set issue.Inheritance_error.c4d

          inheritance1 = c4d.BaseObject(1018775)
          inheritance1_Name = 'Inheritance-'+str(number)+ '_1'
          inheritance1.SetName(inheritance1_Name) # Set the first inheritance name.
          inheritance1.InsertUnder(null) # Insert the first inheritance under the null.
          inheritance1[c4d.MGINHERITANCEEFFECTOR_OBJECT] = cloner # Set the first inheritance Effector Object to the transition cloner.
          inheritance1[c4d.MGINHERITANCEEFFECTOR_MORPHMG] = True # Set the first inheritance Effector Morph Motion Object to True.
          inexcludedata1 = clonerbase[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST] # Get the cloner base Effectors List
          inexcludedata1.InsertObject(inheritance1, 1) # Insert the first inheritance in the Effectors List
          clonerbase[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST] = inexcludedata1 # Set the cloner base Effectors List.
          
          InheritanceID1 = find_DescID(clonerbase, inheritance1_Name)
          Inheritance1Track = c4d.CTrack(clonerbase, InheritanceID1) # Get the CTrack of the base cloner for the first inheritance.
          clonerbase.InsertTrackSorted(Inheritance1Track)
          Inheritance1Curve = Inheritance1Track.GetCurve() # Get the curve of the base cloner for the first inheritance.
          
          time = c4d.BaseTime(frames[0] - 1, doc.GetFps())
          clonerbase[InheritanceID1] = 0.0 # Set the strength of the first inheritance to 0.
          dictKey = Inheritance1Curve.AddKey(time)
          key = dictKey['key']
          Inheritance1Track.FillKey(doc, clonerbase, key) # Set the keyframe of the base cloner first inheritance strength.
          
          time = c4d.BaseTime(frames[0], doc.GetFps())
          clonerbase[InheritanceID1] = 1.0 # Set the strength of the first inheritance to 1.
          dictKey = Inheritance1Curve.AddKey(time)
          key = dictKey['key']
          Inheritance1Track.FillKey(doc, clonerbase, key)# Set the keyframe of the base cloner second inheritance strength.
          

          I have not shared all the code as this code is not meant to be public.

          I also have attached a Cinema 4D file where after the script run the inheritance works, but after a project save, close Cinema 4D reopen Cinema 4D, and the project, the inheritance does not work.

          Inheritance_error.c4d

          1 Reply Last reply Reply Quote 0
          • J
            Joel @ferdinand
            last edited by

            @ferdinand Is the information that I have provided sufficient?

            ferdinandF 1 Reply Last reply Reply Quote 0
            • ferdinandF
              ferdinand @Joel
              last edited by

              Hello @joel,

              it is. I simply did not get to your posting today, my apologies. Will be my first thing on my desk tomorrow.

              Cheers,
              Ferdinand

              MAXON SDK Specialist
              developers.maxon.net

              J 1 Reply Last reply Reply Quote 0
              • J
                Joel @ferdinand
                last edited by

                @ferdinand Thank you so much, looking forward to your reply.

                ferdinandF 1 Reply Last reply Reply Quote 0
                • ferdinandF
                  ferdinand @Joel
                  last edited by ferdinand

                  Hello @joel,

                  so, I had a look at your file and your code. Your file seems broken beyond repair, I was not able to replicate your results there, the tracks themselves seem to be malformed.

                  The problem is here, that you do not show how you construct these track IDs returned by your find_DescID function, which then also leaves me a bit in the dark which parameter you want to keyframe. From the context I assumed that you want to animate the dynamically managed effector list strength values of a cloner.

                  Screenshot 2023-01-24 at 12.45.56.png

                  Without that code, I cannot tell you if you do construct your IDs correctly. What also stands out, is that you do not send c4d. MSG_MENUPREPARE to your Mograph nodes, causing your effectors not to be respected in loaded files. This is the likely cause of your problems. Find below an example script which does work fine for me, both on execution and a saved file.

                  Cheers,
                  Ferdinand

                  File: mg_effector_rig.c4d
                  Result: Screenshot 2023-01-24 at 13.03.29.png
                  Code:

                  """Creates a Mograph rig with a cloner influenced by effectors, then creates a key frame animation
                  for the effector influences on the cloner.
                  
                  Must be run from the Script Manager. Your key problem was probably not sending MSG_MENUPREPARE
                  to the effectors, possibly in combination with constructing the track IDs incorrectly.
                  """
                  import c4d
                  import typing
                  
                  doc: c4d.documents.BaseDocument # The active document.
                  
                  def CreateClonerRig(doc: c4d.documents.BaseDocument) -> c4d.BaseObject:
                      """Creates a MoGraph cloner rig with an inheritance and a random effector.
                      """
                      if not isinstance(doc, c4d.documents.BaseDocument):
                          raise TypeError(f"{doc = }")
                  
                      # Instantiate the cloner, the cloned object, the two effectors and a motion source for the
                      # inheritance effector. I am using here the Mograph symbols exposed with 2023.0, e.g.,
                      # Omgcloner. For prior versions one must define them manually.
                      clonerObj: c4d.BaseObject = c4d.BaseObject(c4d.Omgcloner)
                      cloneObj: c4d.BaseObject = c4d.BaseObject(c4d.Ocube)
                      motionSourceObj: c4d.BaseObject = c4d.BaseObject(c4d.Osphere)
                      inheritanceEffector: c4d.BaseObject = c4d.BaseObject(c4d.Omginheritance)
                      randomEffector: c4d.BaseObject = c4d.BaseObject(c4d.Omgrandom)
                  
                      if None in (clonerObj, cloneObj, motionSourceObj, inheritanceEffector, randomEffector):
                          raise MemoryError("Could not allocate cloner setup.")
                  
                      # Create the Phong and Vibrate tag on the motion source object.
                      phongTag: c4d.BaseTag = motionSourceObj.MakeTag(c4d.Tphong)
                      vibrateTag: c4d.BaseTag = motionSourceObj.MakeTag(c4d.Tvibrate)
                  
                      if None in (phongTag, vibrateTag):
                          raise MemoryError("Could not allocate cloner setup.")
                  
                      # Insert the objects into the document.
                      doc.InsertObject(clonerObj)
                      doc.InsertObject(inheritanceEffector)
                      doc.InsertObject(randomEffector)
                      doc.InsertObject(motionSourceObj)
                      cloneObj.InsertUnder(clonerObj)
                  
                      # Set the size of the cloned cube object, set a position vibration on the Vibrate tag, and
                      # set the source object for the inheritance effector.
                      cloneObj[c4d.PRIM_CUBE_LEN] = c4d.Vector(50)
                      vibrateTag[c4d.VIBRATEEXPRESSION_POS_ENABLE] = True
                      vibrateTag[c4d.VIBRATEEXPRESSION_POS_AMPLITUDE] = c4d.Vector(100)
                      inheritanceEffector[c4d.MGINHERITANCEEFFECTOR_OBJECT] = motionSourceObj
                  
                      # Add both effectors to the cloner.
                      effectorList: c4d.InExcludeData = clonerObj[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST]
                      effectorList.InsertObject(inheritanceEffector, 1)
                      effectorList.InsertObject(randomEffector, 1)
                      clonerObj[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST] = effectorList
                  
                      # It is important to send this message to all Mograph objects on instantiation.
                      for node in (clonerObj, inheritanceEffector, randomEffector):
                          node.Message(c4d.MSG_MENUPREPARE, doc)
                  
                      c4d.EventAdd()
                      return clonerObj
                  
                  def AnimateEffectorList(clonerObj: c4d.BaseObject) -> None:
                      """Creates a keyframe animation for each effector strength in the effector list of the given
                      cloner.
                      """
                      # Make sure the passed cloner is indeed a cloner and attached to a document.
                      if not isinstance(clonerObj, c4d.BaseObject) or clonerObj.GetType() != c4d.Omgcloner:
                          raise TypeError(f"{clonerObj = }")
                  
                      doc: c4d.documents.BaseDocument = clonerObj.GetDocument()
                      if not isinstance(doc, c4d.documents.BaseDocument):
                          raise RuntimeError(f"{clonerObj} is not attached to a document.")
                  
                      # Define the three points in document time to insert keyframes for.
                      fps: int = doc.GetFps()
                      minTime: c4d.BaseTime = doc.GetMinTime() # 1st frame
                      maxTime: c4d.BaseTime = doc.GetMaxTime() # last frame
                      midTime: c4d.BaseTime = c4d.BaseTime(
                          int((maxTime.GetFrame(fps) - minTime.GetFrame(fps)) / 2), fps) # 'middle' frame
                  
                      # Get the effectors which are linked in the cloner. Note that #linkedObjects can contain
                      # None as ObjectFromIndex() can return None. This happens when a link cannot be resolved
                      # anymore, i.e., an InExcludeData list is referencing something which does not exist anymore.
                      effectorList: c4d.InExcludeData = clonerObj[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST]
                      linkedObjects: list[typing.Optional[c4d.BaseObject]] = [
                          effectorList.ObjectFromIndex(doc, i) for i in range(effectorList.GetObjectCount())]
                  
                      # Iterate over all objects in the InEx:
                      for i, effector in enumerate(linkedObjects):
                          # Step over dangling references.
                          if effector is None:
                              continue
                  
                          # Define the ID for the parameter. The parameters are dynamically generated and start of at
                          # ID 1500 and then continue in the order and length of the InExcludeData.
                          did: c4d.DescID = c4d.DescID(c4d.DescLevel(1500 + i, c4d.DTYPE_REAL, c4d.Omgcloner))
                          print (f"{did = }, {effector.GetName() = }")
                  
                          # Get or create a track for the parameter ID.
                          track: typing.Optional[c4d.CTrack] = clonerObj.FindCTrack(did)
                          if track is None:
                              track = c4d.CTrack(clonerObj, did)
                              clonerObj.InsertTrackSorted(track)
                  
                          # Get the curve of the track and start adding keyframes.
                          curve: c4d.CCurve = track.GetCurve()
                          for time, value in ((minTime, 0.), (midTime, .25), (maxTime, 1.)):
                              key: c4d.CKey = curve.AddKey(time)["key"]
                              key.SetValue(curve, value)
                  
                              print (f"Created key for {did} at '{time.Get()}' sec with the value '{value}'.")
                  
                  
                  def main() -> None:
                      """Runs the example.
                      """
                      clonerObj: c4d.BaseObject = CreateClonerRig(doc)
                      AnimateEffectorList(clonerObj)
                  
                      c4d.EventAdd()
                  
                  if __name__ == "__main__":
                      main()
                  

                  MAXON SDK Specialist
                  developers.maxon.net

                  J 1 Reply Last reply Reply Quote 0
                  • J
                    Joel @ferdinand
                    last edited by

                    @ferdinand Thank you so much. Adding:

                    inheritance1.Message(c4d.MSG_MEUPREPARE, doc)
                    

                    Solved the issue.

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