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
    The forum rollback caused push notifications and recent user data to malfunction. The problem will fix itself naturally within the next days. See the topic Broken Push Notifications for a more in detail explanation. You can fix this yourself by forcibly clearing your browser cache (for most browsers: CTRL + F5).

    Timeline commands ran via Python script does not have same result

    Cinema 4D SDK
    2024 python
    3
    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.
    • D
      Daguna
      last edited by Daguna

      Hi!

      This is probably not worthy to be called a developing question, but I think this might be the best place to ask, just please beware that I have zero knowledge in Python or scripting for C4D 🙂

      I'm trying to create a simple workflow script that rewinds the timeline to the start and then steps one frame forward, useful when doing particle sims for example. I then want to assign this script to be triggered by a keyboard shortcut so that these actions can be executed with a single button press instead of two.

      Following basic tutorials I got it somewhat working, but the strange thing is that it yields slightly different results in comparison to if I execute the commands manually. It seems the particle sim is not fully reset if ran via the script, but I have no clue to why that might be. I'm hoping there is an obvious simple thing I have missed, does anyone know?

      from typing import Optional
      import c4d
      
      doc: c4d.documents.BaseDocument  # The active document
      op: Optional[c4d.BaseObject]  # The active object, None if unselected
      
      def main() -> None:
          # Called when the plugin is selected by the user. Similar to CommandData.Execute.
          c4d.CallCommand(12501) # Go to Start
          c4d.EventAdd()
          c4d.CallCommand(12414) # Go to Next Frame
          c4d.EventAdd()
      """
      def state():
          # Defines the state of the command in a menu. Similar to CommandData.GetState.
          return c4d.CMD_ENABLED
      """
      
      if __name__ == '__main__':
          main()
      
      i_mazlovI 1 Reply Last reply Reply Quote 0
      • i_mazlovI
        i_mazlov @Daguna
        last edited by m_adam

        Hello @Daguna , welcome to the Plugin Café forum and the Cinema 4D development community, it is great to have you with us!

        Getting Started

        Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.

        • Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
        • Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
        • Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.

        It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: Asking Questions.

        About your First Question

        It is definitely worth calling a developer question.

        First of all, script execution is a blocking operation and the result of the c4d.EventAdd() (docs) will be postponed. This means, that there's no reason to call it more than once.

        Second of all, you get different results because you need to update the simulation after you have changed the current timeline position. You can easily do this with the ExecutePasses() function. (However, I think running c4d.EventAdd(c4d.EVENT_ANIMATE) once instead of EventAdd() + ExecutePasses() would actually be sufficient here (docs))

        The third thing I would recommend is to calculate the time manually, as in this case you'd first calculate the position and then jump there in one turn, rather than jumping first to the very beginning and then jump one frame further. This is easily done using GetMinTime() function to get the time of the first frame in the document, adding the time that corresponds to one frame of the document (this is calculated based on document's fps setting: GetFps()), and then jump directly to this time using SetTime() function.

        Please find the final script below.

        By the way, there's an example basedocument_animate_r13.py that shows how to run an animation from within the script manager, however, for just a single frame jump the script below is just enough.

        Cheers,
        Ilia

        The script:

        import c4d
        
        doc: c4d.documents.BaseDocument  # The currently active document.
        
        def main() -> None:
            # Get the minimal time of the document (can potentially be different from 0)
            baseTimeMinimal = doc.GetMinTime()
            # Calculate time of the first frame: minimal time + 1 / FPS
            baseTimeFirstFrame = baseTimeMinimal + c4d.BaseTime(1, doc.GetFps())
            # Set time of the first frame to the document
            doc.SetTime(baseTimeFirstFrame)
            # Ask document to recalculate expressions and caches (for the simulation to update)
            doc.ExecutePasses(None, False, True, True, c4d.BUILDFLAGS_NONE)
            # Ask cinema to update all the changes
            c4d.EventAdd()
        
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        D 1 Reply Last reply Reply Quote 0
        • D
          Daguna @i_mazlov
          last edited by

          @i_mazlov Thank you very much for taking the time out to help me with this! Please excuse the late reply, I had neglected to activate email notifications 🙄

          I have now tried the solution you provided but it gives the same result unfortunately.

          Maybe it should be mentioned that I'm working with X-particles mostly, and perhaps something needs to be triggered for it aswell?

          I'm thinking there must be a way to get exactly what these two manual buttons presses do into one script, since it's all code underneath anyhow, right?

          Thanks again!

          DunhouD 2 Replies Last reply Reply Quote 0
          • DunhouD
            Dunhou @Daguna
            last edited by

            Hi @Daguna,

            I think @i_mazlov misunderstand your purpose here, simulation run depend on time and need rest the time to zero, you want -1 here, you can just replace the "+" with "-", the frame will be set to -1 rather than 1.

            Cheers~
            DunHou

            import c4d
            
            doc: c4d.documents.BaseDocument  # The currently active document.
            
            def main() -> None:
                # Get the minimal time of the document (can potentially be different from 0)
                baseTimeMinimal = doc.GetMinTime()
                # Calculate time of the first frame: minimal time - 1 / FPS
                baseTimeFirstFrame = baseTimeMinimal - c4d.BaseTime(1, doc.GetFps())
                # Set time of the first frame to the document
                doc.SetTime(baseTimeFirstFrame)
                # Ask document to recalculate expressions and caches (for the simulation to update)
                doc.ExecutePasses(None, False, True, True, c4d.BUILDFLAGS_NONE)
                # Ask cinema to update all the changes
                c4d.EventAdd()
            
            if __name__ == '__main__':
                main()
            
            

            https://boghma.com
            https://github.com/DunHouGo

            1 Reply Last reply Reply Quote 0
            • DunhouD
              Dunhou @Daguna
              last edited by Dunhou

              Hi @Daguna ,

              Sorry to bad answer, I misread your target, see a new one here:

              from typing import Optional
              import c4d
              
              doc: c4d.documents.BaseDocument  # The active document
              op: Optional[c4d.BaseObject]  # The active object, None if unselected
              
              def main():
              
                  # Retrieves BaseTime of frame 5, 20
                  start = 0
                  end = 1
              
                  # Loops through the frames
                  for frame in range(start, end + 1):
              
                      # Changes the time of the document
                      doc.SetTime(c4d.BaseTime(frame, doc.GetFps()))
              
                      # Updates timeline
                      c4d.GeSyncMessage(c4d.EVMSG_TIMECHANGED)
              
                      # Redraws the viewport and regenerate the cache object
                      c4d.DrawViews(c4d.DRAWFLAGS_ONLY_ACTIVE_VIEW | c4d.DRAWFLAGS_NO_THREAD | c4d.DRAWFLAGS_STATICBREAK)
              
              
              
                  # Pushes an update event to Cinema 4D
                  c4d.EventAdd(c4d.EVENT_ANIMATE)
              
              
              if __name__ == '__main__':
                  main()
              

              https://boghma.com
              https://github.com/DunHouGo

              D 1 Reply Last reply Reply Quote 2
              • D
                Daguna @Dunhou
                last edited by

                Endless thanks @Dunhou, your last version works perfectly!

                Final thing, I realized it needs an if statement to check if the playhead is playing forwards, and if so stop it. Command 12412 seems to be a "toggle" value, and I have tried to figure it out myself but without success.. Any chance you can help me with this last piece of the puzzle aswell?

                My current attempt, that always seems to trigger:

                    if c4d.CallCommand(12412) == True:
                        c4d.CallCommand(12412) # Play Forwards
                        return
                
                DunhouD 1 Reply Last reply Reply Quote 0
                • DunhouD
                  Dunhou @Daguna
                  last edited by

                  Hi @Daguna ,

                  You can check if the timeline is running with c4d.CheckIsRunning(). Just add a condition in before the mian().

                  Cheers~
                  DunHou

                  if __name__ == '__main__':
                      if c4d.CheckIsRunning(c4d.CHECKISRUNNING_ANIMATIONRUNNING):
                          c4d.CallCommand(12412) # Play Forwards
                      main()
                  

                  https://boghma.com
                  https://github.com/DunHouGo

                  D 1 Reply Last reply Reply Quote 1
                  • D
                    Daguna @Dunhou
                    last edited by

                    Marvelous @Dunhou, thanks yet again!!

                    Here's the final script in case someone else has use for it:

                    from typing import Optional
                    import c4d
                    
                    doc: c4d.documents.BaseDocument  # The active document
                    op: Optional[c4d.BaseObject]  # The active object, None if unselected
                    
                    def main():
                    
                        # Retrieves BaseTime of frame 5, 20
                        start = 0
                        end = 1
                        if c4d.CheckIsRunning(c4d.CHECKISRUNNING_ANIMATIONRUNNING) == True:
                            c4d.CallCommand(12412) # Play Forwards
                    
                        # Loops through the frames
                        for frame in range(start, end + 1):
                    
                            # Changes the time of the document
                            doc.SetTime(c4d.BaseTime(frame, doc.GetFps()))
                    
                            # Updates timeline
                            c4d.GeSyncMessage(c4d.EVMSG_TIMECHANGED)
                    
                            # Redraws the viewport and regenerate the cache object
                            c4d.DrawViews(c4d.DRAWFLAGS_ONLY_ACTIVE_VIEW | c4d.DRAWFLAGS_NO_THREAD | c4d.DRAWFLAGS_STATICBREAK)
                    
                        # Pushes an update event to Cinema 4D
                        c4d.EventAdd(c4d.EVENT_ANIMATE)
                    
                    if __name__ == '__main__':
                        main()
                    
                    1 Reply Last reply Reply Quote 1
                    • First post
                      Last post