Solved How to be sure if an object is a generator/modifier?

Hello guys,

as the title says I'm looking for a reliable way to check if an object in the object manager is actually a generator or modifier.

I know I can use e.g. c4d.BaseList2D.GetInfo() & c4d.OBJECT_GENERATOR. While that works with e.g. c4d.BaseObject(c4d.Osds) it doesn't work with c4d.BaseObject(c4d.Olight). A light object doesn't seem to be a generator, is it?

Basically I want a bullet proof way to know if an object is a generator or a modifier and therefore has c4d.ID_BASEOBJECT_GENERATOR_FLAG.

Can this be done?

Cheers,
Sebastian

Hello @HerrMay,

Thank you for reaching out to us. Well, what c4d.BaseList2D.GetInfo() is returning are the plugin flags the plugin has been registered with, e.g., c4d.OBJECT_GENERATOR. What one then considers "bullet proof" is a bit of a question of taste.

c4d.BaseList2D.GetInfo returns the plugin flags mask without any filtering, i.e., it is in this sense bullet proof, and in the sense that it will correctly identify plugins that exhibit the behavior that comes with for example with OBJECT_GENERATOR. What you or the common user associate with these terms might be rightfully something different, and in the end these things are just that: flags. So nothing prevents a developer from registering something as a OBJECT_GENERATOR and then writing something that behaves very differently. But things like Olight and Ocamera rightfully do not classify as generators because they have no cache, the core characteristic of a generator, as also indicated by the documentation:

OBJECT_GENERATOR: Generator object. Produces a polygonal or spline representation on its own. (E.g. primitive cube.)

Noteworthy is in this context that SplineObject instances are generators because they generate their LineObject cache. the Modelling - Geometry Model Python code examples expand a bit more on that concept.

306a3d9d-1b55-4788-bbcc-afde27d29a13-image.png

As an alternative, you could test for objects being a PointObject instance, as all things which are not generators in your world view should be a point object (again, it is a bit ambiguous what you would consider a generator and what not). You can also check if something is a BaseObject but not a PointObject and has no cache to decide if it is something like Olight. What is a modifier or not in this interpretative/analytical sense is probably impossible, as there is already room for interpretation for the native objects of Cinema 4D, deformers and MoGraph effectors are modifiers, field objects are not. This likely extends to many plugin objects which might have modifier qualities but do not necessarily flag themselves as such with OBJECT_MODIFIER.

Regarding ID_BASEOBJECT_GENERATOR_FLAG, all objects have this parameter, even the ones which do not display it. So, calling C4DAtom.GetParameter with the flag DESCFLAGS_GET_NO_GEDATADEFAULTVALUE will not yield None for objects which seem to not have this parameter. But you can inspect the description of an object to find out, see the code example at the end.

Cheers,
Ferdinand

Result:

Cube.1(Polygon) cannot be enabled or disabled.
Group(Group) enabled-state is: 0.
Linear Field(Linear Field) enabled-state is: 1.
Camera(Camera) cannot be enabled or disabled.
Light(Light) enabled-state is: 1.

Code:

"""Demonstrates how to check if a BaseObject has the green enabled check-mark or not.

Will print out the state for the selected object when run.
"""

import c4d
import typing

doc: c4d.documents.BaseDocument  # The active document
op: typing.Optional[c4d.BaseObject]  # The active object, None if unselected


def main() -> None:
    """Runs the example.
    """
    if not op:
        return

    # Something like this will not work, because all BaseObject types carry this parameter, it is 
    # just that some of them are hiding it. So, we will never retrieve None here.
    state: typing.Optional[bool] = op.GetParameter(
        c4d.ID_BASEOBJECT_GENERATOR_FLAG, c4d.DESCFLAGS_GET_NO_GEDATADEFAULTVALUE)
    
    # But we can look up if this parameter is hidden in the description of the object.
    description: c4d.Description = op.GetDescription(c4d.DESCFLAGS_DESC_NONE)
    param: typing.Optional[c4d.BaseContainer] = description.GetParameter(
        c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_GENERATOR_FLAG, c4d.DTYPE_BOOL, 0)))
    if not param:
        raise RuntimeError("Could not retrieve parameter container.")

    isHidden: bool = param[c4d.DESC_HIDE]

    if isHidden:
        print (f"{op.GetName()}({op.GetTypeName()}) cannot be enabled or disabled.")
    else:
        # We could also reuse #state here, but the fancier GetParameter() access is not required.
        print (f"{op.GetName()}({op.GetTypeName()}) enabled-state is: "
               f"{op[c4d.ID_BASEOBJECT_GENERATOR_FLAG]}.")


if __name__ == '__main__':
    main()

MAXON SDK Specialist
developers.maxon.net

Hello @HerrMay,

Thank you for reaching out to us. Well, what c4d.BaseList2D.GetInfo() is returning are the plugin flags the plugin has been registered with, e.g., c4d.OBJECT_GENERATOR. What one then considers "bullet proof" is a bit of a question of taste.

c4d.BaseList2D.GetInfo returns the plugin flags mask without any filtering, i.e., it is in this sense bullet proof, and in the sense that it will correctly identify plugins that exhibit the behavior that comes with for example with OBJECT_GENERATOR. What you or the common user associate with these terms might be rightfully something different, and in the end these things are just that: flags. So nothing prevents a developer from registering something as a OBJECT_GENERATOR and then writing something that behaves very differently. But things like Olight and Ocamera rightfully do not classify as generators because they have no cache, the core characteristic of a generator, as also indicated by the documentation:

OBJECT_GENERATOR: Generator object. Produces a polygonal or spline representation on its own. (E.g. primitive cube.)

Noteworthy is in this context that SplineObject instances are generators because they generate their LineObject cache. the Modelling - Geometry Model Python code examples expand a bit more on that concept.

306a3d9d-1b55-4788-bbcc-afde27d29a13-image.png

As an alternative, you could test for objects being a PointObject instance, as all things which are not generators in your world view should be a point object (again, it is a bit ambiguous what you would consider a generator and what not). You can also check if something is a BaseObject but not a PointObject and has no cache to decide if it is something like Olight. What is a modifier or not in this interpretative/analytical sense is probably impossible, as there is already room for interpretation for the native objects of Cinema 4D, deformers and MoGraph effectors are modifiers, field objects are not. This likely extends to many plugin objects which might have modifier qualities but do not necessarily flag themselves as such with OBJECT_MODIFIER.

Regarding ID_BASEOBJECT_GENERATOR_FLAG, all objects have this parameter, even the ones which do not display it. So, calling C4DAtom.GetParameter with the flag DESCFLAGS_GET_NO_GEDATADEFAULTVALUE will not yield None for objects which seem to not have this parameter. But you can inspect the description of an object to find out, see the code example at the end.

Cheers,
Ferdinand

Result:

Cube.1(Polygon) cannot be enabled or disabled.
Group(Group) enabled-state is: 0.
Linear Field(Linear Field) enabled-state is: 1.
Camera(Camera) cannot be enabled or disabled.
Light(Light) enabled-state is: 1.

Code:

"""Demonstrates how to check if a BaseObject has the green enabled check-mark or not.

Will print out the state for the selected object when run.
"""

import c4d
import typing

doc: c4d.documents.BaseDocument  # The active document
op: typing.Optional[c4d.BaseObject]  # The active object, None if unselected


def main() -> None:
    """Runs the example.
    """
    if not op:
        return

    # Something like this will not work, because all BaseObject types carry this parameter, it is 
    # just that some of them are hiding it. So, we will never retrieve None here.
    state: typing.Optional[bool] = op.GetParameter(
        c4d.ID_BASEOBJECT_GENERATOR_FLAG, c4d.DESCFLAGS_GET_NO_GEDATADEFAULTVALUE)
    
    # But we can look up if this parameter is hidden in the description of the object.
    description: c4d.Description = op.GetDescription(c4d.DESCFLAGS_DESC_NONE)
    param: typing.Optional[c4d.BaseContainer] = description.GetParameter(
        c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_GENERATOR_FLAG, c4d.DTYPE_BOOL, 0)))
    if not param:
        raise RuntimeError("Could not retrieve parameter container.")

    isHidden: bool = param[c4d.DESC_HIDE]

    if isHidden:
        print (f"{op.GetName()}({op.GetTypeName()}) cannot be enabled or disabled.")
    else:
        # We could also reuse #state here, but the fancier GetParameter() access is not required.
        print (f"{op.GetName()}({op.GetTypeName()}) enabled-state is: "
               f"{op[c4d.ID_BASEOBJECT_GENERATOR_FLAG]}.")


if __name__ == '__main__':
    main()

MAXON SDK Specialist
developers.maxon.net

Hello @ferdinand,

alright I see. I already thought that you would teach me once again that the naive thought I put up here would'nt hold up to the real world. :D :D :D

Thanks for the explanation and the example you provided. I guess that will do just fine. As always much appreciated! :)

Cheers,
Sebastian