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

    Other methods for material[c4d.ID_MATERIALASSIGNMENTS]?

    Cinema 4D SDK
    python 2023
    2
    7
    958
    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,

      To get the material assignments, you usually do:
      material[c4d.ID_MATERIALASSIGNMENTS]

      It works but it gives an error (i.e. breaks the code flow) if the material assignments are empty.
      What I would expect is not an error but None.

      I can do it through:

      try:
          material[c4d.ID_MATERIALASSIGNMENTS]
      except:
         # Do something. 
      

      It works but I want to avoid the try/except block and would just a None return instead.

      So are there any other methods aside from material[c4d.ID_MATERIALASSIGNMENTS]?

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

        Hello @bentraje,

        Thank you for reaching out to us. To start with what I would consider most important: Avoiding exception handling for performance reasons is IMHO just C++/programmer folklore and does not make too much sense in Python. If you want to do it, C4DAtom.Get/SetParameter would be the way to go.

        But accessing ID_MATERIALASSIGNMENTS should not fail, the parameter should be always populated. Find below an example script which demonstrates the approach on ID_USERDATA, 1, the first user data parameter in a node, which indeed can be absent.

        Cheers,
        Ferdinand

        Result for running on the default cube:

        Yikes, access error, we scratched the paint on <c4d.BaseObject object called Cube/Cube with ID 5159 at 2574288990272> (parameter access failed).
        C4DAtom.GetParameter = None
        

        Code:

        """Demonstrates how to let parameter access fail gracefully using C4DAtom.GetParameter in contrast
        to GeListNode.__getitem__ access.
        """
        
        import c4d
        
        doc: c4d.documents.BaseDocument  # The active document
        op: c4d.BaseObject | None  # The active object, None if unselected.
        
        def main() -> None:
            """Runs the example.
            """
            if not op:
                return
        
            # Use __getitem__ and error handling. Apart from a subjective aesthetical standpoint, there is
            # nothing objectively wrong with using exceptions IMHO. Exception handling is cheap, exception
            # raising is not, but that does not really matter, as Python is slow as a snail anyways. And
            # I think all that "exceptions are slow"-talk is just folklore that has carried over from C++.
            #
            # See:
            #   https://docs.python.org/3.10/faq/design.html#how-fast-are-exceptions
            try:
                data: any = op[c4d.ID_USERDATA, 1]
                print(f"__getitem__ = {data}")
            except Exception as e:
                print(f"Yikes, access error, we scratched the paint on {op} ({e}).")
        
            # Use C4DAtom.GetParameter instead, it will fail gracefully by returning None when a parameter
            # does not exist.
            data: c4d.MatAssignData | None = op.GetParameter(
                c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), 
                           c4d.DescLevel(1)), 
                c4d.DESCFLAGS_GET_NONE)
            print (f"C4DAtom.GetParameter = {data}")
        
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

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

          @ferdinand

          Thanks for the response. I can't get it though.

          You are assigning it to an object, BaseObject.
          I'm after the BaseMaterial. So the [c4d.ID_USERDATA, 1] is not really something I can use.
          The Assign Tab is not a userdata. It's a built-in data.

          Anyhow how do I convert the [c4d.ID_MATERIALASSIGNMENTS] to a c4d.DescID? Because I think that's the thing I need for the GetParameter.

          6e9493bf-5091-4f29-b853-dbced4d4bb24-image.png

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

            Hey @bentraje,

            First of all, also a BaseMaterial can have user data, any BaseList2D can:

            0bbfe721-0355-459f-be2a-535c00d70ff5-image.png

            And secondly, I intentionally chose [ID_USERDATA, 1], because other than for ID_MATERIALASSIGNMENTS the parameter for the first user data element can actually be not present on a node. A BaseMaterial on the other hand should always carry ID_MATERIALASSIGNMENTS:

            e975585b-53f7-4bf7-8e92-62cc8ca29d9d-image.png

            Anyhow how do I convert the [c4d.ID_MATERIALASSIGNMENTS] to a c4d.DescID?

            c4d.DescID(c4d.ID_MATERIALASSIGNMENTS)

            I understand that you are here on a mission, but what you want to do should not be required for two reasons. Because for once ID_MATERIALASSIGNMENTS should not fail and on the other hand, there is nothing wrong with try: ... except: ... finally: ....

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

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

              @ferdinand

              RE: there is nothing wrong with try: ... except:
              Yep there is nothing. I just want to avoid it.

              RE: c4d.DescID(c4d.ID_MATERIALASSIGNMENTS)
              This works. Didn't realize it is this straightforward.

              Are there any pointers for reconstructing DescID?
              I see some having a tuple within the tuple. Like what you referred above:
              c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)),

              In animation for position parameter, it could be:
              c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION, c4d.DA_VECTOR),c4d.DescLevel(c4d.VECTOR_Z, c4d.DA_REAL, c4d.DA_VECTOR))

              In this case, it is a tuple within a tuple within a tuple.
              That's why I don't usually use GetParameter because I don't actually get the actual parameter descID to use. lol

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

                Hello @bentraje,

                you could have a look at this posting of mine, there I did explain DescId a bit. The C++ Manual can also be helpful.

                Cheers,
                Ferdinand

                MAXON SDK Specialist
                developers.maxon.net

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

                  @ferdinand

                  Gotcha. Thanks. Will close this thread now.

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