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

    Grabbing camera position/rotation from Takes

    Cinema 4D SDK
    r23 python project tool
    2
    4
    861
    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.
    • F
      Futurium
      last edited by

      Hi,
      I spent some time yesterday trying to develop a Python script, which travels through takes, grabbing camera position (which is changed every time by aligning to spline tag), creating a null object and copying camera Matrix to null. The part I'm struggling with is grabbing the camera position. I either got the original camera position, before it's been modified by aligning to spline property or value 0.
      Below is my code and also attached scene, which I've been using for tests.
      Am I missing something obvious?

      import c4d
      from c4d import documents
      
      list_of_takes = []
      parent_name = "360_cams"
      
      def GetCategory(doc):
          takeData = doc.GetTakeData()
          if takeData is None:
              return
      
          mainTake = takeData.GetMainTake()
          take = mainTake.GetDown()
      
          while take is not None:
              list_of_takes.append((take))
              take = take.GetNext()
      
      def CreateParentObject4360CamExport(doc):
          cam_export_parent = doc.SearchObject(parent_name)
          if cam_export_parent is not None:
              cam_export_parent.Remove()
      
          c4d.CallCommand(5140)  # Null
          cam_export_parent = doc.SearchObject("Null");
          c4d.CallCommand(1019940)  # Reset PSR
          if cam_export_parent != None:
              cam_export_parent.SetName(parent_name)
      
          return cam_export_parent
      
      def CreateCopyOfTheCamera(currentCam,currentTake, doc,take_data):
          c4d.CallCommand(5103) # Camera
          camTemp = doc.SearchObject("Camera")
          c4d.CallCommand(1019940)  # Reset PSR
          if camTemp != None:
              camTemp.SetName(currentTake.GetName())
      
      
          print("current cam : "+ currentCam.GetName() + " and position " + str(currentCam.GetMg()))
          camTemp.SetMg(currentCam.GetMg())
          c4d.EventAdd()
          return camTemp
      
      # Main function
      def main():
          doc = documents.GetActiveDocument()
          takeData = doc.GetTakeData()
          if takeData is None:
              raise RuntimeError("No take data in the document.")
          cam_export_parent = CreateParentObject4360CamExport(doc)
          main_take = takeData.GetMainTake()
          GetCategory(doc)
          for child_take in list_of_takes:
              takeLoaded = takeData.SetCurrentTake(child_take)
              if takeLoaded == True:
                  currentCam = child_take.GetCamera(takeData)
                  cam2NullClone = CreateCopyOfTheCamera(currentCam,child_take,doc,takeData)
                  cam2NullClone.InsertUnder(cam_export_parent)
                  c4d.EventAdd()
      
      # Execute main()
      if __name__=='__main__':
          c4d.CallCommand(13957) # Clear Console
          main()
      

      Link to c4d file

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

        Hello @Futurium,

        Thank you for reaching out to us and please excuse the short delay.

        I am struggling a bit with understanding what you want to do exactly, but I assume your major problem is that the objects copied by your script always have the transform the object had when the script was invoked and not the transform it should have according to the activated take. This is caused by you not letting Cinema 4D update when you invoke takeData.SetCurrentTake(child_take). You must call BaseDocument.ExecutePasses on the document containing the activated take when you want more complex scene information to be updated immediately, e.g, the "Align To Spline" tag which is driving the camera in your rig.

        There are also other problems with your code ranging from the overuse of CallCommand (you should avoid using it), over only looking at top-level takes, to a code structure that at least I do not fully understand. I have provided a code snippet which does what I think you want to be done at the end of this posting.

        Cheers,
        Ferdinand

        The result:
        cam_takes.gif

        The code:

        """Copies the object named '360 Camera 2' for each take state in the document into a null object.
        """
        
        import c4d
        import typing
        
        def AssertType(item: any, t: typing.Type, label: str) -> None:
            """Asserts that #item is of type #t. When the assertion fails, an assertion error will be 
            raised with #label referring to #item.
            """
            if not isinstance(item, t):
                raise AssertionError(f"Expected {t} for '{label}'. Received: {type(item)}")
        
        def GetDocumentTakes(doc: c4d.documents.BaseDocument) -> c4d.modules.takesystem.BaseTake:
            """Yields all non main-take takes in #doc.
        
            Doing it like this is necessary because your variant only yields takes that are directly parented
            to the main take. Which works for the file provided by you but would fail on something like this:
        
                MainTake
                    Take.0
                    Take.1
                        Take.2
                    Take.3
        
            As your variant would then only return [Take.0, Take.1, Take.3].
            """
            AssertType(doc, c4d.documents.BaseDocument, "doc")
        
            takeData = doc.GetTakeData()
            mainTake = takeData.GetMainTake()
        
            def iterate(node):
                """Walks #node depth first.
                """
                if node is None:
                    return
        
                while node:
                    yield node
                    for descendant in iterate(node.GetDown()):
                        yield descendant
                    node = node.GetNext()
        
            for take in iterate(mainTake.GetDown()):
                yield take
        
        
        def main(doc: c4d.documents.BaseDocument):
            """Copies the object named '360 Camera 2' for each take state in the document into a null object.
        
            The copying is done manually without C4DAtom.GetCLone(), following somewhat what you did.
            """
            # Get the take data of the document.
            takeData = doc.GetTakeData()
        
            # Get the camera target object.
            camera = doc.SearchObject("360 Camera 2")
        
            # A null object to parent the new cameras to.
            null = c4d.BaseObject(c4d.Onull)
            if null is None:
                raise MemoryError("Could not allocate null object.")
            doc.InsertObject(null)
        
            # Iterate over all takes in the document.
            for take in GetDocumentTakes(doc):
                # Set the currently yielded take as the active take.
                if not takeData.SetCurrentTake(take):
                    raise RuntimeError("Could not activate take.")
                # Execute the passes on the document so that the take can take effect. You could pass 
                # only #True for #expressions for your scene, since you only want to evaluate "align-to-spline" 
                # tag, but I set here more stuff to #True in case you want to do more.
                doc.ExecutePasses(bt=None, animation=True, expressions=True, 
                                  caches=True, flags=c4d.BUILDFLAGS_NONE)
        
                # I assume you want to copy here the #camera object without its tags, but preserve the
                # name, transform, and parameter values of the object (otherwise you could just use 
                # C4DAtom.GetCLone()). 
        
                # We could also hard-code the camera object type with c4d.Ocamera instead of #camera.
                # But with GetType() it will just copy whatever object type is #camera. When you set #camera 
                # to a null object, how you implicitly did in your script, this still work.
                cameraCopy = c4d.BaseObject(camera.GetType())
                if cameraCopy is None:
                    raise MemoryError("Could not allocate camera object.")
        
                # Copy the name and transform of the camera.
                cameraCopy.SetName(f"{camera.GetName()}({take.GetName()})")
                cameraCopy.SetMg(camera.GetMg())
        
                # Copy over the parameters of the camera (focus, zoom, etc). We can do this since we can
                # be sure that they are of identical type.
                cameraCopy.SetData(camera.GetData())
        
                # Insert the new camera copy.
                cameraCopy.InsertUnder(null)
        
            # Set the active take (back) to the main take.
            takeData.SetCurrentTake(takeData.GetMainTake())
        
            # Push an update event to Cinema 4D.
            c4d.EventAdd()
        
        if __name__ == "__main__":
            main(doc)
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • F
          Futurium
          last edited by

          Thank you, Ferdinand.

          It looks like the only part I missed in my code was that extra line

          doc.ExecutePasses(bt=None, animation=True, expressions=True, caches=True, flags=c4d.BUILDFLAGS_NONE)
          

          After updating the code on my side - everything seems to be working fine.
          Thank you again.
          Best regards,
          Tomasz

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

            Hello @Futurium,

            without any further questions and other postings, we will consider this topic as solved and flag it as such by Friday, 17/06/2022.

            Thank you for your understanding,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

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