Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python 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

    Create Xpresso Node with input port Object

    Cinema 4D SDK
    python
    2
    7
    225
    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.
    • J
      JoelJohera
      last edited by

      To whom it may concern,
      I am trying to create an Xpresso tag and nodes programmatically with python.
      I can create a Sphere with custom user data, like a link where I will drag another object. I can create the sphere object with output ports for its global position and the link.
      For my application, I need to obtain the global position of the object that will be linked in the link.
      I can create an object node; however, I can not programmatically add the Object Input Port to the object node.
      How is the correct way to add this Input Port Object ? Is there any other way to extract the global position of the link?

      Also, is there a way to set a node name programmatically?

      doc = c4d.documents.GetActiveDocument()
      sphere = c4d.BaseObject(c4d.Osphere)
      doc.InsertObject(sphere)
      
      ud_target = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK)
      ud_target[c4d.DESC_NAME] = "Target"
      ud_target[c4d.DESC_ANIMATE] = c4d.DESC_ANIMATE_ON
      sphere.AddUserData(ud_target)
      
      xtag = c4d.BaseTag(c4d.Texpresso)
      sphere.InsertTag(xtag)
      gv = xtag.GetNodeMaster()
      
      sphere_node = gv.CreateNode(gv.GetRoot(),c4d.ID_OPERATOR_OBJECT)
      sphere_node[c4d.GV_OBJECT_OBJECT_ID] = sphere
      global_pos_port = sphere_node.AddPort(c4d.GV_PORT_OUTPUT, c4d.ID_BASEOBJECT_GLOBAL_POSITION)
      user_data_container = sphere.GetUserDataContainer()
      target_desc_id = None
      target_port = None
      for desc_id, bc in user_data_container:
         if bc[c4d.DESC_NAME] == "Target":
            target_desc_id = desc_id
            target_port = sphere_node.AddPort(c4d.GV_PORT_OUTPUT, desc_id)
      
      target_node = gv.CreateNode(gv.GetRoot(), c4d.ID_OPERATOR_OBJECT)
      target_object_input = target_node.AddPort(c4d.GV_PORT_INPUT, c4d.GV_OBJECT_OBJECT_ID)
      

      What I get with the above code:
      f869aa4f-f2e0-44ad-aa9e-5bee98bf57ec-image.png
      What I need it to do:
      648b31b6-2bbb-451a-a449-e21c874a1333-image.png

      To later link the target with the object.

      Thank you for your time.
      Joel Johera

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

        Hello @JoelJohera,

        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: How to Ask Questions.

        About your First Question

        Please make sure to post executable code in the future. It was just some imports and a main function missing here, but non-executable code always bears the risk of you being misunderstood and usually means that we do not try to run your code. Your code is mostly correct. Your major fault is that the ID for the input port for an object reference is GV_OBJECT_OPERATOR_OBJECT_IN and not GV_OBJECT_OBJECT_ID (that is the ID for the description parameter).

        There is also a minor problem with how Xpresso updates the labels of object nodes which had set their wrapped scene element via connection. But this is not an issue of the Python API, but rather a general issue with Xpresso.

        Find my cleaned up solution below.

        Cheers,
        Ferdinand

        Result

        7d0a3d89-2033-4a78-abb6-4532c5a9d983-image.png

        Code

        import c4d
        import mxutils
        
        from c4d.modules.graphview import GvNode, GvPort, GvNodeMaster, XPressoTag
        from mxutils import CheckType
        
        doc: c4d.documents.BaseDocument  # The currently active document.
        op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
        
        def main() -> None:
            """Called by Cinema 4D when the script is being executed.
            """
            # All these #CheckType calls make sure that the return value of something is not None. The
            # allocation of scene elements can fail, and it is advisable to check. See the docs for details.
        
            # Create a sphere and a cone object, create an XPresso tag on the sphere, and then create the 
            # parameter description for a baselink named 'Target'.
            sphere: c4d.BaseObject = CheckType(c4d.BaseObject(c4d.Osphere))
            cone: c4d.BaseObject = CheckType(c4d.BaseObject(c4d.Ocone))
            tag: XPressoTag = CheckType(sphere.MakeTag(c4d.Texpresso))
            settings: c4d.BaseContainer = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK)
            settings[c4d.DESC_NAME] = "Target"
        
            # Add the baselink parameter to the user data of the sphere object and link the cone object in 
            # it, then insert both objects into the document.
            pid: c4d.DescID = sphere.AddUserData(settings)
            sphere[pid] = cone
            doc.InsertObject(sphere)
            doc.InsertObject(cone)
        
            # Get the Xpresso root node and create two nodes in the graph. One for the sphere object and
            # one for object linked in the baselink parameter of the sphere object. Finally, link the
            # sphere object as the reference object of the sphere node.
            master: GvNodeMaster = tag.GetNodeMaster()
            root: GvNode = master.GetRoot()
        
            sphereNode: GvNode = CheckType(master.CreateNode(root, c4d.ID_OPERATOR_OBJECT))
            targetNode: GvNode = CheckType(master.CreateNode(root, c4d.ID_OPERATOR_OBJECT))
            sphereNode[c4d.GV_OBJECT_OBJECT_ID] = sphere
        
            # Now create an object output port on the sphere node and an object input port on the target 
            # node and connect the two. The caveat is here that unless we first manually set the port 
            # value, Xpresso does not seem to update the label of the node when we set the connection. I.e.,
            # the node will not be labeled 'Cone'. This issue persists when we later manually change the
            # object in the user data of the sphere object. And it also happens when we create the setup
            # entirely manually from scratch in the Xpresso editor. So, this seems to be more a general
            # issue with Xpresso and not its Python API.
            #
            # But this does not impact the technical functionality of the setup, it still always affects the
            # correct node, it is just the label on the node that is misleading.
            objOutPort: GvPort = CheckType(sphereNode.AddPort(c4d.GV_PORT_OUTPUT, pid))
            objInPort: GvPort = CheckType(targetNode.AddPort(c4d.GV_PORT_INPUT, c4d.GV_OBJECT_OPERATOR_OBJECT_IN))
            targetNode[c4d.GV_OBJECT_OBJECT_ID] = cone # Preset the port value to update the label.
            objInPort.Connect(objOutPort) # Connect the two ports.
        
            # To demonstrate this, we create a port for the global transform of the object wrapped by the
            # target node and set the position of the object.
            transformInPort: GvPort = CheckType(targetNode.AddPort(c4d.GV_PORT_INPUT, 
                                                                   c4d.GV_OBJECT_OPERATOR_GLOBAL_IN))
            targetNode[c4d.GV_OBJECT_OPERATOR_GLOBAL_IN,c4d.MATRIX_OFF] = c4d.Vector(300, 300, 0)
            
            # Push an update event.
            c4d.EventAdd()
        
        
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • J
          JoelJohera
          last edited by

          Thank you for the information.

          Also where I can find all the GV_ options and ID_ options for creating nodes, and ports ?

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

            Hey,

            the overview for GV ops can be found here here, accessible from our types and symbols index. For the dedicated resources you would have then to look into our resource index, specifically in the graph view section. There you will then find the parsed resources for operators and could look for example at the Object Operator.

            The caveat is that there is a small irregularity with the gv object node as it redefines some of its resource parameters for their input and output port. The only other operator type which does seem to do that is hair collision operator for its velocity port. It has likely something to do with the fact that both these operators wrap scene elements. But the resources document all these ports.

            If you just want a plain technical top-down view on symbols you can also look at /{c4d}/resource/modules/python/libs/python311/c4d/__init__.py. There you can see it is only a handful (20 to be exact) ports which follow this _IN|_OUT postfix scheme:

            b0587150-1cbd-49a2-bc61-2375b7d41197-image.png

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • J
              JoelJohera
              last edited by

              Thank you so much for the resources. I asked because now I am trying to create a Python node, and I tried to specify the input port as c4d.DTYPE_VECTOR, but it does not work.

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

                Hey @JoelJohera,

                Please stop asking non-follow-up questions in a thread, when you have a new question, please open a new thread.

                With that being said:

                30e628c3-42e4-4242-a388-031c677bcb67-image.png

                    # Create a Python node.
                    pythonNode: GvNode = CheckType(master.CreateNode(root, c4d.ID_OPERATOR_PYTHON))
                
                    # Create a vector input port on it. The syntax for the dynamic ports of a Python node is a bit
                    # weird and I also only found out how this works by dragging parameters to the console and by
                    # inspecting the description of the node.
                    vectorInPort: GvPort = CheckType(pythonNode.AddPort(
                        c4d.GV_PORT_INPUT,
                        # The plain symbols, e.g., #GV_OBJECT_OPERATOR_OBJECT_IN are only a short hand form for how
                        # parameter IDs are defined under the hood: DescIDs. There are plenty of postings about this
                        # on the forum, just search for 'DescID' and my user name, one is for example here:
                        # https://developers.maxon.net/forum/topic/14342/howto-set-keyframes-for-posxyz-rotxyz-scalexyz-with-a-script/15
                        c4d.DescID(
                            # The IDs for the dynamic ports are really weird. The first level is of type sub-container,
                            # and has an ID such as IN_VECTOR, OUT_VECTOR, IN_REAL, OUT_REAL, etc. The second level
                            # then describes the the actual data and its ID. The IDs seem to start at 1001, and since
                            # a Python node has two default ports, I am putting mine at 1003. The levels also have
                            # some truly wild owners (the third parameter of a level which set here to 0). I checked
                            # its not the type ID of the op. But as so often, you can also null the owner ID and it
                            # still works.
                            c4d.DescLevel(c4d.IN_VECTOR, c4d.DTYPE_SUBCONTAINER, 0),
                            c4d.DescLevel(1003, c4d.DTYPE_VECTOR, 0)
                
                            # These IDs are in fact so weird, that our parameter drag and drop misfires and produces
                            # malformed DescIDs (when you press enter on such dragged parameter, it raises an
                            # AttributeError). But dragging them can still be useful to figure these a bit hand-wavy
                            # type symbols such as IN_VECTOR, OUT_VECTOR, etc.
                        )
                    ))
                

                Cheers,
                Ferdinand

                MAXON SDK Specialist
                developers.maxon.net

                1 Reply Last reply Reply Quote 0
                • J
                  JoelJohera
                  last edited by

                  Ok thanks and sorry

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