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

    retrieve data from multiple cloners in python

    Cinema 4D SDK
    2
    10
    2.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.
    • P
      Parvin
      last edited by m_adam

      Hi,
      I'm sorry if I'm posting a repeated topic. I am trying to get color data from multiple mo-graph in python to send out to a micro controller for realtime animation.
      I am explaining what I've done so far
      I have 90 linear cloners of cubes (there are 30 of them in each cloner). I want to get color data from each clone and send the rgb color to the controller. After a couple of hours searching I came to this conclusion to make a blank 2D list, append each cloner as an object to the list and therefore, I can have an ID to retrieve the data from. from my one week experience in C4D and python, I wrote this code. I set 2 cloners as user data. this it absolutely not working. If any one has done sth similar to what I want to do, his/her help would be much appreciated.

      import c4d
      from c4d.modules import mograph as mo
      from c4d import utils
        
      def main(): 
          n = 2
          m = 33
          Matrix = [[0] * m for i in range(n)]
         
          Obj1 = op[c4d.ID_USERDATA,2]
          Obj2 = op[c4d.ID_USERDATA,1]
          
          count = len(Obj1) 
          print count
          
          for j in range (32): 
             Matrix[j][0] = Obj1
             Matrix[j][1] = Obj2 
             
          md = mo.GeGetMoData(op)
          if md==None: return False
          
          cnt = md.GetCount()
          marr = md.GetArray(c4d.MODATA_MATRIX)
          carr = md.GetArray(c4d.MODATA_COLOR)
          
          print carr
             
          for i in reversed(xrange(0, cnt)):
             md.SetArray(c4d.MODATA_COLOR, carr, True)
             md.SetArray(c4d.MODATA_WEIGHT, warr, True)
             return True
      
      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by m_adam

        Hi @Parvin, first of all, welcome in the plugincafe community!

        Regarding your requestion, a cloner is simply a generator that will copy objects in its cache (like any generator), so in order to access the correct data, you should first access the cache of the first cloner then here you can iterates the cloner and query them for their color like so.

        """
        Copyright: MAXON Computer GmbH
        Author: Maxime Adam
        
        Description:
            - Retrieves the color or each clone using two ways.
            - First one by accessing directly the Mograph Data.
            - Second one by accessing the polygon cache representation of the Mograph Cloner.
        
        Class/method highlighted:
            - c4d.modules.mograph.GeGetMoData()
            - c4d.modules.mograph.MoData
            - MoData.GetArray()
            - BaseObject.GetCache()
            - BaseObject.GetDeformCache()
        
        Compatible:
            - Win / Mac
            - R16, R17, R18, R19, R20
        """
        import c4d
        
        
        def RetrieveColorWithMoData(op):
            # Retrieving internal cache of the first cloner to access each cloner and query them.
            cacheMainClone = op.GetCache()
            if cacheMainClone is None:
                raise RuntimeError("Failed to retrieves the cached cloner.")
        
            # Cloner object creates a null, with all their clones. So we dive into this null, and it should be a cloner object
            clonerCache = cacheMainClone.GetDown()
            if not clonerCache.CheckType(1018544):
                raise TypeError("objects are not cloners.")
        
            # Iterates each cloner and retrieves the data for each one
            finalList = []
            while clonerCache:
                # Retrieves the modata
                md = c4d.modules.mograph.GeGetMoData(clonerCache)
                if md is None:
                    continue
        
                # Retrieves the clone offset and the color array
                offset = clonerCache[c4d.MG_LINEAR_OFFSET]
                colorList = md.GetArray(c4d.MODATA_COLOR)
        
                # Appends the color list taking in account the offset (aka skip the first elements)
                finalList.append(colorList[offset:])
        
                # Process the next cloner
                clonerCache = clonerCache.GetNext()
        
            return finalList
        
        
        def DeformedPolygonCacheIterator(op):
            """
            A Python Generator to iterate over all PolygonCache of passed BaseObject
            :param op: The BaseObject to retrieves all PolygonObject cache.
            """
            if not isinstance(op, c4d.BaseObject):
                raise TypeError("Expected a BaseObject or derived class got {0}".format(op.__class__.__name__))
        
            # Try to retrieves the deformed cache of the object
            temp = op.GetDeformCache()
            if temp is not None:
                # If there is a deformed cache we iterate over him, a deformed cache can also contain deformed cache
                # e.g. in case of a nested deformer
                for obj in DeformedPolygonCacheIterator(temp):
                    yield obj
        
            # Try to retrieves the cache of the Object
            temp = op.GetCache()
            if temp is not None:
                # If there is a cache iterate over its, a cache can also contain deformed cache
                # e.g. an instance, have a cache of its linked object but if this object is deformed, then you have a deformed cache as well
                for obj in DeformedPolygonCacheIterator(temp):
                    yield obj
        
            # If op is not a generator / modifier
            if not op.GetBit(c4d.BIT_CONTROLOBJECT):
                # If op is a PolygonObject we return it
                if op.IsInstanceOf(c4d.Opolygon):
                    yield op
        
            # Then finally iterates over the child of the current object to retrieves all objects
            # e.g. in a cloner set to Instance mode, all clones is a new object.
            temp = op.GetDown()
            while temp:
                for obj in DeformedPolygonCacheIterator(temp):
                    yield obj
                temp = temp.GetNext()
        
        
        def RetrieveColorWithCache(op):
            # Iterates the polygon cache of a cloner (does work only, in case of simple instance mode)
            childClonerCnt = op.GetDown()[c4d.MG_LINEAR_COUNT]
            finalList = []
            # Iterates overs each polygon object cache
            for i, obj in enumerate(DeformedPolygonCacheIterator(op)):
                # For each new list we add a new list
                d = float(i) / float(childClonerCnt)
                if float(d) == int(d):
                    finalList.append([])
        
                # Adds the object information in the last list
                finalList[-1].append(obj[c4d.ID_BASEOBJECT_COLOR])
        
            return finalList
        
        
        def main():
            # Checks if there is a selected object, and this selected object get a child object.
            if not op or not op.GetDown():
                raise RuntimeError("Failed to retrieves op and its child.")
        
            # Checks if selected object and child objects are cloner objects.
            if not op.CheckType(1018544) or not op.GetDown().CheckType(1018544):
                raise TypeError("objects are not cloners.")
        
            print RetrieveColorWithMoData(op)
            print RetrieveColorWithCache(op)
        
        
        if __name__ == "__main__":
            main()
        
        

        Don't worry since it's your first topic, in order to setup correctly your next topics (I've done it for this one) please read and apply:

        • Q&A New Functionality.
        • How to Post Questions.

        Of course, if you have any question, please let me know.
        Cheers,
        Maxime.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

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

          Wow! thanks Adam. I have to take my time to process all the useful information you gave me. I will reply on this post if I have further questions.

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

            @m_adam when I ran the code you posted, I got the error "Failed to retrieves op and its child". I am calling each function in the main to realize what each of them is doing. But I think all of them are linked to one another.
            So my main question is that when you say I need to select my objects in order, how should I exactly do this? I put the Python Effector in the effectors list for each 90 cloners that I have, and I assumed this would work.

            Thanks,
            Parvin

            1 Reply Last reply Reply Quote 0
            • M
              m_adam
              last edited by

              Hoo sorry, I overlooked the fact that you are directly in a python effector and not in the script manager (in fact I missed to ask you, please next time try to say us, in which context you are executing python).

              So I should have mentioned that the selected object should be the topmost cloner (the cloner of the cloner).
              So with that's said, with my previous scrip use "gen" instead of op in main and it's fixed see Python Effector.
              Here is the example cloner.c4d

              Cheers,
              Maxime.

              MAXON SDK Specialist

              Development Blog, MAXON Registered Developer

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

                Oh I see. I take that note for python effector, thanks. I think I explained it in a very wrong way because as I see you have a cloner as the child of other cloner. In my example, I have 90 different cloners. I will attach my example.

                P

                90 strut_Animation.c4d

                1 Reply Last reply Reply Quote 0
                • M
                  m_adam
                  last edited by

                  Then I'm afraid I still don't understand the final result, please be more explicit about what you are trying to achieve, what the expected behavior. In your first post, you said it's not working, but now I do have a scene example so please tell me what you want to achieve within the scene.

                  Cheers,
                  Maxime.

                  MAXON SDK Specialist

                  Development Blog, MAXON Registered Developer

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

                    I have 90 strut shaping a sphere in this scene. each strut is a linear cloner of cubes and they represent LED strips. I want to animate the light in realtime. So, I am trying to animate the sphere with effectors and change the color, then send the color data through Lumos Library to a Raspberry Pi to animate light. I'm awfully new to C4D and Python in C4D.
                    I know I have to give address to each cube and link it with the address I have for the LED pixel. But that goes way further. what I'm struggling with now is capturing data of each cloner.

                    Thanks for your patience,
                    Parvin

                    1 Reply Last reply Reply Quote 0
                    • M
                      m_adam
                      last edited by

                      I don't think effector is the best way to achieve that at least your effector could retrieve data only for the current generator/object it's currently sampling, so one object per time. So an effector does not have an idea of the whole picture.

                      The best way would be either to have a GeDialog that will monitor the scene and send the data (but that involve to make a plugin, it's not complicated but its an external file and not directly embedded within a C4D file)
                      Another solution could be to do a scripting tag, with the correct priority (so it's executed after mograph generation) which will retrieve all data you need. You could add an InExclude UserData to this tag so could drag and drop the cloner in the list and use this as an ID.

                      Anyway I adapted the example to make it work, with simple cloner (no nested), so the op object of the main method should be a cloner and it will tell you the color for each clone.

                      """
                      Copyright: MAXON Computer GmbH
                      Author: Maxime Adam
                      
                      Description:
                          - Retrieves the color or each clone using two ways.
                          - First one by accessing directly the Mograph Data.
                          - Second one by accessing the polygon cache representation of the Mograph Cloner.
                      
                      Class/method highlighted:
                          - c4d.modules.mograph.GeGetMoData()
                          - c4d.modules.mograph.MoData
                          - MoData.GetArray()
                          - BaseObject.GetCache()
                          - BaseObject.GetDeformCache()
                      
                      Compatible:
                          - Win / Mac
                          - R16, R17, R18, R19, R20
                      """
                      import c4d
                      
                      
                      def RetrieveColorWithMoData(op):
                          if not op.CheckType(1018544):
                              raise TypeError("objects is not a cloner.")
                      
                          # Retrieves the modata
                          md = c4d.modules.mograph.GeGetMoData(op)
                          if md is None:
                              return []
                      
                          # Retrieves the clone offset and the color array
                          offset = op[c4d.MG_LINEAR_OFFSET]
                          colorList = md.GetArray(c4d.MODATA_COLOR)
                      
                          # Appends the color list taking in account the offset (aka skip the first elements)
                          return colorList[offset:]
                      
                      
                      def DeformedPolygonCacheIterator(op):
                          """
                          A Python Generator to iterate over all PolygonCache of passed BaseObject
                          :param op: The BaseObject to retrieves all PolygonObject cache.
                          """
                          if not isinstance(op, c4d.BaseObject):
                              raise TypeError("Expected a BaseObject or derived class got {0}".format(op.__class__.__name__))
                      
                          # Try to retrieves the deformed cache of the object
                          temp = op.GetDeformCache()
                          if temp is not None:
                              # If there is a deformed cache we iterate over him, a deformed cache can also contain deformed cache
                              # e.g. in case of a nested deformer
                              for obj in DeformedPolygonCacheIterator(temp):
                                  yield obj
                      
                          # Try to retrieves the cache of the Object
                          temp = op.GetCache()
                          if temp is not None:
                              # If there is a cache iterate over its, a cache can also contain deformed cache
                              # e.g. an instance, have a cache of its linked object but if this object is deformed, then you have a deformed cache as well
                              for obj in DeformedPolygonCacheIterator(temp):
                                  yield obj
                      
                          # If op is not a generator / modifier
                          if not op.GetBit(c4d.BIT_CONTROLOBJECT):
                              # If op is a PolygonObject we return it
                              if op.IsInstanceOf(c4d.Opolygon):
                                  yield op
                      
                          # Then finally iterates over the child of the current object to retrieves all objects
                          # e.g. in a cloner set to Instance mode, all clones is a new object.
                          temp = op.GetDown()
                          while temp:
                              for obj in DeformedPolygonCacheIterator(temp):
                                  yield obj
                              temp = temp.GetNext()
                      
                      
                      def RetrieveColorWithCache(op):
                          # Iterates the polygon cache of a cloner (does work only, in case of simple instance mode)
                          finalList = []
                          # Iterates overs each polygon object cache
                          for i, obj in enumerate(DeformedPolygonCacheIterator(op)):
                      
                              # Adds the object information in the last list
                              finalList.append(obj[c4d.ID_BASEOBJECT_COLOR])
                      
                          return finalList
                      
                      
                      def main():
                          # Checks if there is a selected object, and this selected object get a child object.
                          if not op:
                              raise RuntimeError("Failed to retrieves op")
                      
                          # Checks if selected object and child objects are cloner objects.
                          if not op.CheckType(1018544):
                              raise TypeError("objects is not cloner.")
                      
                          print RetrieveColorWithMoData(op)
                          print RetrieveColorWithCache(op)
                      
                      
                      if __name__ == "__main__":
                          main()
                      
                      

                      With that's said I think you have everything you need to build your stuff, at least for reading a cloner object.

                      The project looks cool, so even if it seems complicated don't give up and don't hesitate to ask questions ! 😉
                      Cheers,
                      Maxime.

                      MAXON SDK Specialist

                      Development Blog, MAXON Registered Developer

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

                        Thank you @m_adam it's really helpful! I was going to a wrong direction. I chose to do a project that has every new aspect to explore :)) I will for sure disturb you in future with my questions. I appreciate your suggestions.

                        Parvin

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