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

    How can I use a render token to indicate the render type?

    Cinema 4D SDK
    r25 python windows
    2
    4
    552
    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.
    • G
      GordOrb
      last edited by

      Hi there,
      I've been having trouble with c4d's render tokens. I'm trying to make a token that will change based on the render type (multipass or regular), but the tokens always evaluate to a regular image render, even when I'm not rendering a regular image. See my code here for a further breakdown:

      """Multipass token problem.
      
      Goal is to have a render token that can dynamically change based on the
      current render type (multipass or regular image). What's confusing is that
      tokens are evaluated about 3 times when the project is added to the render
      queue, then an additional 11-14 times during the actual render. The token used
      for multipass renders is the second token that is evaluated during the actual
      render, which occurs before multipass is active. Around the 6th or 7th
      evaluation, multipass becomes active (and this evaluates correctly), but the
      correct multipass token is never used in the file name.
      
      """
      import c4d
      from datetime import datetime
      
      
      # A hypothetical dict with info specific to the render type.
      dynamic_token_dict = {"multipass": "multi", "regular": "reg"}
      
      
      def get_render_type(data) -> str:
          """Supposed to return the render type. Multipass or Regular image.
      
          Returns:
              The render type + the time the token was made.
      
          """
          print("Getting token...")
          print(data[7])  # Pass id for multipass renders or NOTOK if not multipass
          dynamic_token = dynamic_token_dict[
              "regular" if data[7] == c4d.NOTOK else "multipass"
          ]
          now = str(datetime.now())
          print(dynamic_token + now)
          return dynamic_token + now
      
      
      if __name__ == "__main__":
          token_set = set(
              token_dict["_token"]
              for token_dict in c4d.modules.tokensystem.GetAllTokenEntries()
          )
          if "r_type" not in token_set:
              c4d.plugins.RegisterToken(
                  "r_type", "[Render] Type", "reg/multi", get_render_type
              )
      
      

      Is this possible? Also, why do tokens evaluate so many times? Thanks in advance.
      -G

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

        Hello @GordOrb,

        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

        Well, the docs tell you that data[7] will not be populated all the time. And yes, plugin functions get often called more often than it might look like from the outside world.

        data[7] (int) The pass ID used for rendering or NOTOK if multipass is not active or not yet recognized.

        You could try to use the fields data[5] or data[6] (the pass names/labels), but that is just some other info which has to be populated by something and might not be there all the time. When possible, I would always go over the document which is being passed, because there you can be sure that data is set. E.g., something like this:

        import c4d
        import mxutils
        
        def eval_token_is_multipass(data: dict[str, any]) -> str:
            """Evaluates an %is_multipass token.
            """
            doc: c4d.documents.BaseDocument = mxutils.CheckType(data[0], c4d.documents.BaseDocument)
            return "multipass" if doc.GetActiveRenderData()[c4d.RDATA_MULTIPASS_ENABLE] else "regular"
        

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • G
          GordOrb
          last edited by GordOrb

          Hi @ferdinand,

          Thank you for the introduction and for welcoming me to this forum. Glad to be part of the community.

          In my testing, I found that data[5], data[6], and data[7] were all set at the same time (around the 6th or 7th evaluation), so I don't think I can use that to determine the type of render. This makes me wonder, how is an api user supposed to use those data values in any meaningful way when the render token evaluated before the data is populated?

          I appreciate the code you provided, but I don't think that solves my question either. Please correct me if I'm wrong, but my understanding is that you can kick off a render job that outputs BOTH a regular image and multi-pass images in the same job. The example you gave indicates if multipass is enabled in the render settings, but this will also be true during the job's regular image render (so the regular image filename will incorrectly indicate a multipass render). Please see the attached image for how I expect this to be used.
          Screenshot 2024-02-07 at 12.22.18 PM.png
          Thanks again,
          -G

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

            Hey @GordOrb,

            This is not possible. You are not really meant to distinguish the file names you are writing for. I also do not understand the purpose of doing what you want to do. You can already name the beauty file and the multipass file differently in the UI. And when you write out each pass on its own, Cinema 4D will already name them differently for you.

            There will be singular token call for all pass layers, and before that a call for the RGB/beauty layer. None of the calls for naming the file contains pass information. The inner logic of it seems to be that you first get the call for evaluating the token for a filename and after that the actual passes.

            The purpose of some fields in the passed data is probably more of internal than public nature, since you cannot really do much with that info due to how the token calls are coming in. In some contexts these fields might be useful when you use the Token System (C++ Manual) more manually. But at least for the passes case, this is not really true either, because when you start to render manually, you will have to fill in the pass data on your own when constructing a RenderPathData.

            So, long story short: this is not really possible, at least in the intended way. Upon having a closer look at the time stamps and my file output, I realized that you could try to rely on the rule 'the call after the call for the RGB pass', because that is at least how it works in my example.

            isMultipass: '1', pname: '    ', ptype: '     ', pid: ' -1', t: 9366346949848 // First call, irrelevant
            isMultipass: '1', pname: '    ', ptype: '     ', pid: ' -1', t: 9367113676312 // Time stamp of saved RGB/beauty.
            isMultipass: '1', pname: ' RGB', ptype: '  RGB', pid: ' -1', t: 9367113987757 // RGB/beauty pass
            isMultipass: '1', pname: '    ', ptype: '     ', pid: ' -1', t: 9367114252401 // Time stamp of all passes.
            ...
            

            If this works, this would be hack, and it would be then up to you establish if that holds true or not. To establish "what is the last call", you could have some global list into which you push things. Make sure not to store any Cinema 4D rendering data in that global list (BaseDocument, BaseContainer, RenderData) or you might end up in world of hurt.

            Cheers,
            Ferdinand

            Written Files:
            fc970227-90fb-437e-b5d7-b7a25a9e2da0-image.png
            My console dump, here I search for the time stamp 9367114252401 of my files, it is the fourth call, long before we get the calls for the passes. The only thing that comes before is a call for the RGB, i. e., the beauty pass.
            b5e92449-2ffa-4d4b-a0b7-de02968635d8-image.png
            Code:

            """
            """
            import c4d
            import mxutils
            import time
            
            def get_render_type(data: c4d.BaseContainer) -> str:
                """Dumps information about #data into the token and the console.
                """
                # Get the render document #rdoc, and build a string for the UUID of rdoc as well as its memory 
                # location and #data. Unsurprisingly, the mem locs are meaningless here in Python as everything
                # must be wrapped by Python objects in each call. In C++ you might be able to establish "sameness"
                # between calls by the location of #data (the rendering document which is used is always the
                # same as shown by its UUID). But I doubt that you will have luck with #data in C++, because it
                # is likely constructed for each call there too (and not reused).
                rdoc: c4d.documents.BaseDocument = mxutils.CheckType(data[0], c4d.documents.BaseDocument)
                uuid: bytes = bytes(rdoc.FindUniqueID(c4d.MAXON_CREATOR_ID))
                ident: str = f"doc-uuid: {uuid}, doc-mem: {id(rdoc)}, bc-mem: {id(data)}"
            
                # Build the token.
                token: str = (f"isMultipass: '{rdoc.GetActiveRenderData()[c4d.RDATA_MULTIPASS_ENABLE]}', " 
                              f"pname: '{data[5]:>20}', ptype: '{data[6]:>10}', pid: '{data[7]:>3}', t: {time.perf_counter_ns()}")
                
                print (ident, token)
                return token
            
            if __name__ == "__main__":
                if "r_type" not in set(item["_token"] for item in c4d.modules.tokensystem.GetAllTokenEntries()):
                    c4d.plugins.RegisterToken("r_type", "[Render] Type", "reg/multi", get_render_type)
            
            

            MAXON SDK Specialist
            developers.maxon.net

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