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

    Storing a part of the hierarchy

    Cinema 4D SDK
    r20 c++
    3
    20
    2.0k
    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.
    • P
      pim
      last edited by

      I want to store a part of the hierarchy in a external (hf) file.
      For example, an object with multiple levels of children, materials, etc.

      I can put it in a new scene file and store that scene file, but perhaps there are other / better ways?

      The ultimate goal is to store a part of the hierarchy in a tag plugin using node.write() and node.read().

      1 Reply Last reply Reply Quote 0
      • ManuelM
        Manuel
        last edited by Manuel

        hello,

        you can use the function IsolateObjects than use SaveDocument or SaveProject

        You can read it with LoadDocument to a temporary document inside memory.

        Be careful, if you want to modify the active scene, you have to do it from the main thread otherwise you scene could be in an uncertain state leading to crash.
        you can read information on how to deal with that case here

        Cheers,
        Manuel

        MAXON SDK Specialist

        MAXON Registered Developer

        1 Reply Last reply Reply Quote 0
        • P
          pim
          last edited by

          Thanks, again I learned another function (IsolateObjects).
          If I use LoadDocument to store it in memory, how can I store that memory in the scene file, when the user saves the screen file?
          I am going to use the tagplugin, so I guess that is all right?

          -Pim

          1 Reply Last reply Reply Quote 0
          • ManuelM
            Manuel
            last edited by

            hello,

            what kind of functionality are you trying to introduce ? Using a tag doesn't look like the right place to do what you are trying to do.

            Cheers,
            Manuel

            MAXON SDK Specialist

            MAXON Registered Developer

            1 Reply Last reply Reply Quote 0
            • P
              pim
              last edited by

              I will send you a pm with more details.

              -Pim

              1 Reply Last reply Reply Quote 0
              • ManuelM
                Manuel
                last edited by Manuel

                you can send us an email at [email protected]

                MAXON SDK Specialist

                MAXON Registered Developer

                1 Reply Last reply Reply Quote 0
                • ManuelM
                  Manuel
                  last edited by

                  hello,

                  One possibility that i forgot to mention is MemoryFileStructure Manual

                  And ReadHyperFile or WriteHyperFile

                  You also have with a HyperFile the function WriteFilename

                  Hope this will help.

                  Cheers,
                  Manuel.

                  MAXON SDK Specialist

                  MAXON Registered Developer

                  1 Reply Last reply Reply Quote 0
                  • P
                    pim
                    last edited by pim

                    I want to store the node to be hidden in the scene file, so I guess hf wil not work for me.
                    In the other thread I learned to use IsolateObjects and that works now.

                    So now I have a new doc in memory with the node I wanted to store.
                    I tried to put it in a subcontainer, but that did not work.
                    When reading back I just got None.

                    import c4d
                    
                    PLUGIN_ID = 1234567
                    MyUniqueId = 456789
                    
                    def main():
                        #retrieves the document baseContainer
                        docBC = doc.GetDataInstance()
                    
                        sel = doc.GetSelection()    # !!! only select the parent, not the children
                        newDoc = c4d.documents.IsolateObjects(doc, sel)
                    
                        #create a sub-BaseContainer
                        subBc = c4d.BaseContainer()
                        subBc[1000] = "hello"
                        subBc[2000] = newDoc
                    
                        # Add the container to the "main" Container
                        docBC.SetContainer(MyUniqueId, subBc)
                    
                        # Updates the document container
                        doc.SetData(docBC)
                    
                        # Print the values stored in our container.
                        for cid, value in doc.GetDataInstance().GetContainer(MyUniqueId):
                            print cid, value
                    
                        # Print Hello World
                        print doc[MyUniqueId][1000], doc[MyUniqueId][2000]
                    
                    
                    if __name__=='__main__':
                       main()
                    

                    Output:

                    1000 hello
                    2000 <c4d.documents.BaseDocument object called '' with ID 110059 at 0x00000173601BD370>
                    hello <c4d.documents.BaseDocument object called '' with ID 110059 at 0x00000173601BD330>
                    

                    Reading back the container:

                    import c4d
                    
                    PLUGIN_ID = 1234567
                    MyUniqueId = 456789
                    
                    def main():
                        #retrieves the document baseContainer
                        docBC = doc.GetDataInstance()
                    
                    
                        # Print the values stored in our container.
                        for cid, value in doc.GetDataInstance().GetContainer(MyUniqueId):
                            print cid, value
                    
                        # Print Hello World
                        print doc[MyUniqueId][1000], doc[MyUniqueId][2000]
                    
                    
                    if __name__=='__main__':
                       main()
                    

                    Output:

                    1000 hello
                    2000 None
                    hello None
                    
                    1 Reply Last reply Reply Quote 0
                    • ferdinandF
                      ferdinand
                      last edited by ferdinand

                      Hi,

                      the following line

                      subBc[2000] = newDoc
                      

                      will store a BaseLink (a reference, a pointer) to newDoc in your container, not the object itself. While Python's important syntax for bracket operators (__setitem__, __getitem__) usually is really convenient, in the case of BaseContainers it can obscure what you are actually doing. The verbose form of your call would be:

                      subBc.SetLink(2000, newDoc)
                      

                      Which works because BaseDocument is derived from BaseList2D and therefor also from C4DAtom (the type BaseContainer.SetLink() does expect as its second argument). However, when this link is not properly serialized on saving the document (which is impossible with your floating document), this link would be a null pointer when the document is loaded again. You cannot store a C4DAtom in a BaseContainer. At least I am not aware of a way to do so. You can only store references, which need to be kept alive in order to resolve.

                      On a side note: This whole hijacking of the documents data container should not work in the first place, i.e. Cinema will just clear it out the container at your ID. I am a bit confused that you can actually read back your "hello" on a saved document.

                      Cheers
                      zipit

                      MAXON SDK Specialist
                      developers.maxon.net

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

                        Hi,

                        One thing I forgot, which I already wanted to mention in your other threads, as it is a common technique to deal with similar problems in c4d. Are you aware that you can hide a GeListNode from the users view (so they won't show up in the Object Manager, the Material Manager, etc.) with their NBIT flags? This would be an easy way to store whatever you want to store. You won't believe all the hidden stuff that is floating around in your documents 😉

                        For details check GeListNode.ChangeNBit().

                        Cheers
                        zipit

                        MAXON SDK Specialist
                        developers.maxon.net

                        P 1 Reply Last reply Reply Quote 0
                        • P
                          pim @ferdinand
                          last edited by pim

                          @zipit, I got the code from another thread: https://developers.maxon.net/forum/topic/11802/writing-data-to-the-c4d-file/19
                          And if you change

                          subBc[2000] = "world!"
                          

                          to

                          subBc[2000] = op
                          

                          it also works!

                          I tried

                          subBc.SetLink(2000, newDoc)
                          

                          and indeed it does not work. It returns a None on reading back the subcontainer after storing the file.

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

                            @zipit, yes I know that functionality (and use it often).
                            However, although it is hidden it still consumes cpu power.
                            We like to minimalize cpu power by converting a node using csto, hide the node and disabling generators and deformers in the node.

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

                              Hi,

                              subBc[2000] = "world!"
                              

                              to

                              subBc[2000] = op
                              

                              That is expected as op is also an C4DAtom.

                              it also works!

                              I tried

                              subBc.SetLink(2000, newDoc)
                              

                              and indeed it does not work. It returns a None on reading back the subcontainer after storing the file.

                              The method SetLink() does not have a (boolean) return type. So this is to be expected too. I am not quite sure if you got my point, so here is a modified version of your first script - which you assume to be "working" as it printed back your document. I did modify it in such a way, that it does not work any more - I am really good at this specific task ;). I hope the code and the comments make more clear what I am trying to convey: That you are not storing your document, but a link to it.

                              import c4d
                              
                              PLUGIN_ID = 1234567
                              MyUniqueId = 456789
                              
                              def main():
                                  #retrieves the document baseContainer
                                  docBC = doc.GetDataInstance()
                              
                                  sel = doc.GetSelection()    # !!! only select the parent, not the children
                                  newDoc = c4d.documents.IsolateObjects(doc, sel)
                              
                                  #create a sub-BaseContainer
                                  subBc = c4d.BaseContainer()
                                  subBc[1000] = "hello"
                                  subBc[2000] = newDoc
                                  
                                  # Get the type of the ID we set
                                  print "ID 2000 is a link", subBc.GetType(2000) == c4d.DA_ALIASLINK
                                  
                                  # do the same thing in green. That SetLink() returns None
                                  # is expected as it has no return value (which is implictly
                                  # None in Python)
                                  subBc.SetLink(2000, newDoc)
                                  
                                  print "ID 2000 is a link", subBc.GetType(2000) == c4d.DA_ALIASLINK
                              
                                  # Add the container to the "main" Container
                                  docBC.SetContainer(MyUniqueId, subBc)
                              
                                  # Updates the document container
                                  doc.SetData(docBC)
                              
                                  # Print the values stored in our container.
                                  for cid, value in doc.GetDataInstance().GetContainer(MyUniqueId):
                                      print cid, value
                                      
                                  print "\nDeleting newDoc...\n"
                                  # This part is a bit dodgy since Pythons garbage collector cannot
                                  # be trusted. But what I am trying to show is that you stored
                                  # a reference, not an object. For me it works here, but in some cases
                                  # an object can linger even after marking it for garbage collection.
                                  newDoc.Flush()
                                  del(newDoc)
                                  
                                  # Print the values stored in our container.
                                  for cid, value in doc.GetDataInstance().GetContainer(MyUniqueId):
                                      print cid, value
                              
                              
                              if __name__=='__main__':
                                 main()
                              

                              this should print out something like:

                              ID 2000 is a link True
                              ID 2000 is a link True
                              1000 hello
                              2000 <c4d.documents.BaseDocument object called '' with ID 110059 at 0x00000135BC7F9030>
                              
                              Deleting newDoc...
                              
                              1000 hello
                              2000 None
                              >>> 
                              

                              Cheers
                              zipit

                              MAXON SDK Specialist
                              developers.maxon.net

                              1 Reply Last reply Reply Quote 1
                              • P
                                pim
                                last edited by

                                Thanks for the explanation.

                                Two questions:

                                1. the line <c4d.documents.BaseDocument object called '' with ID 110059 at 0x00000135BC7F9030> always show a strange name ''? Why is it not showing the name of the document?

                                2. Back to the main questions, what to do, to store the node in scene file.

                                ferdinandF 1 Reply Last reply Reply Quote 0
                                • ManuelM
                                  Manuel
                                  last edited by Manuel

                                  hello,

                                  1 - what kind of strange name ?

                                  2 -

                                  you can store the data itself in the tag and than with the function read and write store them in the Scenefine
                                  I'm using a file on the disk on this example but that should work with the HyperFile link provide by the read and write functions.

                                  Be aware that HyperFile.WriteMemory is storing byte sequences that will be platform dependent.

                                  import c4d
                                  from c4d import gui
                                  # Welcome to the world of Python
                                  import os
                                  
                                  
                                  # Main function
                                  def main():
                                      
                                      path =  c4d.storage.LoadDialog(c4d.FILESELECTTYPE_SCENES, flags=c4d.FILESELECT_DIRECTORY)
                                      path = os.path.join(path, "prout.txt")
                                      
                                      # using a MemoryFileStructure to store a document
                                      mfs = c4d.storage.MemoryFileStruct()
                                      mfs.SetMemoryWriteMode()
                                      
                                      newdoc = c4d.documents.IsolateObjects(doc , [op])
                                      # Save the document to the MemoryFileStructure
                                      c4d.documents.SaveDocument(newdoc, mfs, c4d.SAVEDOCUMENTFLAGS_NONE, c4d.FORMAT_C4DEXPORT)
                                      
                                      #Retrieve the data and store it somewhere, could be self.myData
                                      myData = mfs.GetData()
                                      
                                     
                                     
                                      #Save the data to a hyperfile
                                      myFile = c4d.storage.HyperFile()
                                      if not myFile.Open(0, path, c4d.FILEOPEN_WRITE, c4d.FILEDIALOG_NONE):
                                          raise RuntimeError("Failed to open the HyperFile in write mode.")
                                      
                                      # Store the size
                                      myFile.WriteInt32(myData[1])
                                      # Store the data itself
                                      if myFile.WriteMemory(myData[0]) is False:
                                          raise ValueError("can't write the file")
                                      myFile.Close()
                                  
                                      
                                      # Read the data from the HF
                                      if not myFile.Open(0, path, c4d.FILEOPEN_READ, c4d.FILEDIALOG_NONE):
                                          raise RuntimeError("Failed to open the HyperFile in read mode.")
                                      
                                      size = myFile.ReadInt32()
                                      data = myFile.ReadMemory()
                                      myFile.Close()
                                      
                                      #Set the MFS
                                      mfs2 = c4d.storage.MemoryFileStruct()
                                      mfs2.SetMemoryReadMode(data,size)
                                      
                                       
                                      c4d.documents.MergeDocument(doc, mfs2, c4d.SCENEFILTER_OBJECTS)
                                      c4d.EventAdd()
                                  
                                  # Execute main()
                                  if __name__=='__main__':
                                      main()
                                  

                                  Once again, be aware of where you are modifying the scene (on main thread and not elsewhere)

                                  by the way, are you going to use c++ or python at the end ? (just for the tags of this thread)

                                  Cheers,
                                  Manuel

                                  MAXON SDK Specialist

                                  MAXON Registered Developer

                                  1 Reply Last reply Reply Quote 1
                                  • P
                                    pim
                                    last edited by

                                    Great, thank you very much.
                                    I am beginning to see the light.

                                    And yes, my fault, apparently I indicated it as c++, but I am doing it in Python.

                                    About your warning.
                                    My plan was to do it all in a tag plugin.
                                    There I can have the interface, do the isolate and the read/write in a hf.
                                    Or is it better to do it in a Object plugin?

                                    9f1b1f56-1bff-4864-ba56-8cf0c4036897-image.png

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

                                      Hi,

                                      @pim said in Storing a part of the hierarchy:

                                      1. the line <c4d.documents.BaseDocument object called '' with ID 110059 at 0x00000135BC7F9030> always show a strange name ''? Why is it not showing the name of the document?

                                      Not quite sure what you do mean by that:

                                      1. The fact that the it prints the empty string for the document name is probably because you run the script on a unsaved document. This whole untitled_x.c4d stuff you see in c4d app is all smoke and daggers. An unsaved document will return the empty string for GetName().
                                      2. If you mean how objects are printed out, this is because of the way it is convention to implement __repr__() for an object. I found that obsession with memory addresses also always quite bizarre, but, hey, everyone is doing it 😉

                                      Cheers
                                      zipit

                                      MAXON SDK Specialist
                                      developers.maxon.net

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

                                        @pim said in Storing a part of the hierarchy:

                                        My plan was to do it all in a tag plugin.
                                        There I can have the interface, do the isolate and the read/write in a hf.
                                        Or is it better to do it in a Object plugin?

                                        Hi, it does not really matter what kind of NodeData you use, what @m_magalhaes meant, was that you should be careful with the threaded context of methods like Execute() in TagData or GetVirtualObjects() in ObjectData. When you execute your code from a method that is executed from the main thread, you are fine (e.g. NodeData.Message()). You can always check there with c4d.threading.GeIsMainThread() if you are in the main thread to be extra sure.

                                        Cheers
                                        zipit

                                        MAXON SDK Specialist
                                        developers.maxon.net

                                        1 Reply Last reply Reply Quote 0
                                        • ManuelM
                                          Manuel
                                          last edited by Manuel

                                          An ObjectData (generator) would be a bit better for that. But you are exploring possibilities, that's also the way to find new solutions / workflow.

                                          About my warning it's just a reminder. You will have so sent message probably and use a MessageData to react to that message in order to do some action in the main thread.

                                          Cheers,
                                          Manuel

                                          MAXON SDK Specialist

                                          MAXON Registered Developer

                                          1 Reply Last reply Reply Quote 0
                                          • P
                                            pim
                                            last edited by

                                            Thanks for all the support.
                                            I will use all the knowledge gained and start testing.
                                            I am sure, I will be back with more questions.

                                            -Pim

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