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

    GetCache Does Not Work Inside the Python Generator

    Cinema 4D SDK
    r21 r25 python
    2
    10
    1.6k
    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.
    • B
      bentraje
      last edited by

      Hi,

      I have a Text object inside the Python Generator. I want to "bake" down (or equivalent to current state object commant) the text object so I can access it further but the GetCache seems to return none.

      # inside the python generator
      def main() -> c4d.BaseObject:
          text = c4d.BaseObject(1019268)
          text[c4d.PRIM_TEXT_TEXT] = "Generator"
      
          print (text.GetCache()) # returns None
          clone = text.GetClone()
          print (clone.GetCache()) # returns None
      
          # I also tried the `SendModellingCommand` but it gives me `False` instead of `True`
          base = c4d.utils.SendModelingCommand(c4d.MCOMMAND_CURRENTSTATETOOBJECT,[clone],c4d.MODELINGCOMMANDMODE_ALL,c4d.BaseContainer(),clone.GetDocument())
      
          print (clone) # returns False instead of True
      

      I'm guessting there is a prerequisite on the operation.
      What am I missing? 😢 😢 😢

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

        Hey @bentraje,

        Thank you for reaching out to us. There are two different mistakes in your code:

        1. Caches do not exist for freshly allocated objects. They must be built in a document, see geometry_caches_s26.py for an overview of the subject.
        2. Objects must be part of a document to be the subject of a modeling operation. While you did technically the right thing here, called GeListNode.GetDocument instead of blindly passing a document clone might or might not be a member of, the fact remains that clone is not yet a member of a document. See smc_extrude_s26.py for a principal overview of SMC operations.

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 1
        • B
          bentraje
          last edited by

          Hi @ferdinand

          Thanks for the respond.

          RE: Caches do not exist for freshly allocated objects
          Ah gotcha. Thanks for the confirmation.
          Based on your reply, I can do it through a python script (i.e. insert the object temporarily and just destroy it or something like that).

          But how do I go about it on Python Generator object? Is it possible?
          Ultimately, what I want is to have a procedural text object that allows me to modify the extrude parameter of each letter. Currently, the extrude parameter of a default text object applies to all letters.

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

            Hey @bentraje,

            look at the examples I have posted, it is all exactly layed out there 🙂

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • B
              bentraje
              last edited by

              Hi @ferdinand

              I'm not sure I get it. The examples are for Python Script/Manager and not for Python Generator (which is what I am after).

              Anyhow, I'm guessing you are referring to GetCaches(node: c4d.BaseObject) from geometry_caches_s26.py

              If so I did try it in the Python Generator (not in the Python Script):

              def PrintCacheTree():
                 ...
              
              def GetCaches():
                 ...
              
              def main():
                  text = c4d.BaseObject(1019268)
                  GetCaches(text)
                  return text
              

              The result still gives me none, of which I am unable to access the individual extrude parameters of each letters.

              Retrieving Caches:
              
              Text.GetCache() = None
              Text.GetDeformCache() = None
              
              Cache Tree of 'Text':
              
               Text(Text)
              
              Building Caches:
              
              Cache for newly allocated object: None
              Cache after executing the cache pass: <c4d.PolygonObject object called Cube/Polygon with ID 5100 at 2640357790656>
              
              
              ferdinandF 1 Reply Last reply Reply Quote 0
              • ferdinandF
                ferdinand @bentraje
                last edited by ferdinand

                Hey @bentraje,

                I understand that one has not always the time to get into the details of things, and that this file might look a bit much, but you will have to read it in order to understand caches. You must build the cache of your Text object, the script unpacks this bit by bit.

                Concretely you will have to call BaseDocument.ExecutePasses for a document holding your object, but I cannot help you when you just blindly copy and paste the script without trying to understand what it does.

                Cheers,
                Ferdinand

                MAXON SDK Specialist
                developers.maxon.net

                B 1 Reply Last reply Reply Quote 0
                • B
                  bentraje @ferdinand
                  last edited by

                  Hi @ferdinand

                  I admit I'm on the slow side. It's a bit hard to comprehend on my level.

                  Can I get a baseline answer (yes or no) on:

                  The examples are for Python Script/Manager and not for Python Generator (which is what I am after).
                  Is it doable in Python Generator?
                  

                  I can execute your script just find. It gives me caches etc. But when it comes to generator it does not.
                  For example, as soon as I add BaseDocument.ExecutePasses the document freezes

                  def PrintCacheTree():
                     ...
                  
                  def GetCaches():
                     ...
                  
                  def main():
                      text = c4d.BaseObject(1019268)
                      doc.InsertObject(text)
                      doc.ExecutePasses(None, False, False, True, c4d.BUILDFLAGS_NONE) 
                      GetCaches(text)
                      return text
                  
                  ferdinandF 1 Reply Last reply Reply Quote 0
                  • ferdinandF
                    ferdinand @bentraje
                    last edited by ferdinand

                    Hey @bentraje,

                    you are bound by the threading restrictions in the main function of a Python Generator object, you are despite its name off-main-thread there and cannot modify the active scene. Both example scripts expand on this and that you have to use dummy documents in these cases to execute SMC or built the caches of an object.

                    Inserting your text into doc (a.k.a. the active document) will result in infinite recursion on top of that (your freeze). Because doc contains your Python Generator object pygen. When you now insert text into doc from pygen.main() (i.e., the cache building function of pygen), and then execute the passes on doc, it will try to build the cache for pygen. Which will then run pygen.main() again, which will insert text into doc, and then try to build the caches on doc, which will then ...

                    You must allocate a dummy document in your main function. Then insert all the newly allocated objects you want to build the caches for into this dummy document and execute the passes on it. Then you can clone the caches of these objects to return the clones as the output of your generator.

                    Again, both script files which I have posted highlight this exact workflow.

                    I wrote this blindly as I am currently sitting in a train with no Cinema 4D or code editor:

                    import c4d
                    import mxutils
                    
                    from mxutils import CheckType
                    
                    def main():
                        # Allocate the text object and a document. Use CheckType to make sure to fail gracefully when
                        # allocation goes wrong. Insert #text into #temp. 
                        text = CheckType(c4d.BaseObject(1019268))
                        temp = CheckType(c4d.documents.BaseDocument())
                        temp.InsertObject(text)
                        
                        # Execute the passes on temp.
                        temp.ExecutePasses(c4d.threading.GeGetCurrentThread(), True, True, True, c4d.BUILDFLAGS_NONE) 
                        
                        # Clone the cache, making sure with CheckType that the cache exists and then that the
                        # cloning did not fail.
                        data: c4d.BaseObject = CheckType(CheckType(text.GetCache()).GetClone(c4d.COPYFLAGS_NONE))
                    
                        # text and temp are destroyed automatically after the function scope has been left.
                        # Since Python's GC can be a bit derpy you can also call BaseDocument.Flush to streamline
                        # things.
                        temp.Flush()
                    
                        return data
                    

                    Cheers,
                    Ferdinand

                    MAXON SDK Specialist
                    developers.maxon.net

                    B 1 Reply Last reply Reply Quote 1
                    • B
                      bentraje @ferdinand
                      last edited by

                      @ferdinand

                      Thanks for the response. Your code works without the program freezing up (although I think it's GeGetCurrentThread instead of GetCurrentThread()). Other than that, it works as expected.

                      Ah. I didn't think to just pass objects from different documents.

                      I was thinking of something like this:

                      1. All in the same document. Create a dummy object in the same document. In this way, the cache is already created because it now exist.
                      2. Clone it. Store the data.
                      3. Delete the dummy object in the same document.

                      The problem was that in #3, the generator no longer has access to that object for some reason. So it keeps creating a dummy object lol

                      Anyway, enough for my excuses. Sorry for the trouble and thanks again for your patience.

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

                        Hey @bentraje,

                        I am glad that you found your solution, and yes, you are absolutely right, it should be GeGetCurrentThread . I fixed that in my pseudo-code.

                        Cheers,
                        Ferdiand

                        MAXON SDK Specialist
                        developers.maxon.net

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