Solved Other methods for material[c4d.ID_MATERIALASSIGNMENTS]?

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]?

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

@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

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

@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

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

@ferdinand

Gotcha. Thanks. Will close this thread now.