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

    Get all BaseListLink parameters using specific BaseObject

    Cinema 4D SDK
    python 2024
    2
    4
    686
    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.
    • C
      curtwart
      last edited by

      Hey guys, I'm new to working in Cinema 4D and the Cinema 4D python SDK, I am trying to relink a BaseObject into DTYPE_BASELISTLINK parameters after running a join on a BaseObject. I'm wondering if there is a simple way to get all other objects a single BaseObject is linked within, something like a get all objects a BaseObject is referenced by. I have a code snippet below that hopefully demonstrates what I'm trying to achieve.

      def join_object(obj : c4d.BaseObject):
          # Gets the objects that the obj is used in
          linked_objects = get_references(obj)
      
          # Join the object, this will remove the obj from all of its linked parameters of type DTYPE_BASELISTLINK
          new_obj = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_JOIN, list=[obj], doc=doc)[0]
          doc.InsertObject(new_obj, None, obj)
          obj.Remove()
      
          # Relink the new_obj into where the obj was used
          relink_object(linked_objects, obj)
      
          return new_obj
      
      i_mazlovI 2 Replies Last reply Reply Quote 0
      • i_mazlovI
        i_mazlov @curtwart
        last edited by

        Hi @curtwart,

        Welcome to the Maxon developers forum and its 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

        Please excuse the delay. I'm currently a little busy trying to meet all the deadlines. I'll get back to your topic ASAP!

        Could you please in the meantime clarify if you're trying to do something special (i.e. handle relinking with some custom logic) or you're more interested in a general approach of objects not losing their references?

        The reason I'm asking is because keeping references up-to-date is a well-known challenge and there can be multiple ways of handling it. In Cinema C++ API there's a dedicated approach, which involves using AliasTrans class, that does the actual traversal and relinking automatically. However, AliasTrans doesn't have a python binding, hence one cannot use it in our Python API.

        Cheers,
        Ilia

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • i_mazlovI
          i_mazlov @curtwart
          last edited by i_mazlov

          Hi @curtwart,

          You need to manually search for the dependencies. You can do this by accessing object's description and searching for the type DTYPE_BASELISTLINK. I've pasted a very draft script below. I've stolen the tree traversal function from Ferdinand's answer here.

          Please also note, that BaseLink is not the only way to keep the reference of the object, e.g. it can be addressed in the FieldList or in the InExcludeData.

          Cheers,
          Ilia

          The script traverses all the objects on the scene (you should think if checking objects only is enough), for each objects iterates over its descriptions and if DTYPE_BASELISTLINK dound, checks if it refers to our object. All these refs are stored in a list. Then the command is executed, and then the reference objects are revisited to updated the baselink value to a new object.

          import c4d
          from typing import Iterator
          
          doc: c4d.documents.BaseDocument  # The currently active document.
          op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
          
          # Is stolen from: https://developers.maxon.net/forum/topic/15611/how-to-scale-all-objects-in-a-scene-at-once/2
          def IterateTree(node: c4d.GeListNode, yieldSiblings: bool = False) -> Iterator[c4d.GeListNode]:
              """ Iterates over all descendants of the given #node and optionally its next siblings. """
              def iterate(node: c4d.GeListNode) -> Iterator[c4d.GeListNode]:
                  if not node:
                      return
                  yield node
                  for child in node.GetChildren():
                      yield from iterate(child)
              while node:
                  yield from iterate(node)
                  node = node.GetNext() if yieldSiblings else None
          
          def iterateObjectBaselistLinks(op: c4d.BaseObject) -> Iterator[c4d.DescID]:
              """ Searches for all BaseListLinks in the object and returns the list of corresponding DescIDs. """
              for description in op.GetDescription(c4d.DESCFLAGS_DESC_NONE):
                  bc, paramId, groupId = description
          
                  isBaselistLink: bool = False
                  for descLevelIdx in range(paramId.GetDepth()):
                      descLevel: c4d.DescLevel = paramId[descLevelIdx]
                      if descLevel.dtype == c4d.DTYPE_BASELISTLINK:
                          isBaselistLink = True
                          break
                  
                  if not isBaselistLink:
                      continue
                  yield paramId
          
          def findAllObjectReferences(doc: c4d.documents.BaseDocument, op: c4d.BaseObject) -> Iterator[tuple[c4d.BaseObject, c4d.DescID]]:
              """ Iterates over all objects in the document and returns the objects that reference the given object. """
              for rootObject in doc.GetObjects():
                  for obj in IterateTree(rootObject):
                      if obj == op:  # Skipping ourselves, but this might not be correct for some certain cases
                          continue
                      for linkID in iterateObjectBaselistLinks(obj):
                          if obj[linkID] == op:
                              yield obj, linkID
          
          def main() -> None:
              # FIXME: this only checks for a selected object, but you're likely interested in checking for all children as well
              linkedObjectsInfo: list[tuple[c4d.BaseObject, c4d.DescID]] = list(findAllObjectReferences(doc, op))
          
              new_obj = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_JOIN, list=[op], doc=doc)[0]
              doc.InsertObject(new_obj, None, op)
              op.Remove()
          
              # Relink
              for obj, linkID in linkedObjectsInfo:
                  obj[linkID] = new_obj
          
          if __name__ == '__main__':
              main()
              c4d.EventAdd()
          

          MAXON SDK Specialist
          developers.maxon.net

          1 Reply Last reply Reply Quote 0
          • C
            curtwart
            last edited by

            Thank you for this write up, I started writing something similar, but this is a huge help understanding how to get and set the data for BaseLink. I'll need to add FieldList and InExcludeData as you mentioned, knowing the getting and adding the new object is a little more involved than just setting the property id directly. I'll reach out here again if I have any followups.

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