Hello,
An object like BaseObject can store data. This data is typically stored in the object's BaseContainer. But there are cases where the data is not stored in the BaseContainer so in general it is saver to use GetParameter().
A BaseContainer can store any kind of data. This includes other BaseContainers. The BaseContainer of the object stores a sub-BaseContainer that stores all the values of the user data. The ID of the User-Data-BaseContainer is c4d.ID_USERDATA. Using this ID you can obtain the BaseContainer:
bc = op.GetData()
userDataValues = bc[c4d.ID_USERDATA]
# or
userDataValues = op.GetParameter(c4d.ID_USERDATA, c4d.DESCFLAGS_GET_0)
This BaseContainer stores the parameter values of user data parameters.
A parameter is identified using a DescID. This DescID has nothing to do with UUID.
A DescID is composed of one or several DescLevel components. In most cases the DescID has only one DescLevel. The DescLevel defines these properties:
- id: id of the level. Typically the parameter ID.
- dtype: type of the parameter.
- creator: ID of the "creator" of the parameter. E.g. on a cube you have some parameters that were created by the "Cube" (Size, Segments, ...). But there are also other parameters that are shared by all BaseObject's (Position, Scale, ...). These shared parameters are inherited from a base class, in this case Obase (5155). The "creator" value is not always relevant so it might not always be set.
A DescID can have multiple DescLevels since a parameter may be stored inside another parameter. As described above, user data values are stored in a BaseContainer that is stored in the default BaseContainer. The full DescID of a user-data parameter has to reflect that. The DescID of a user-data parameter must first identify the user-data container and then the actual parameter in that container. This is done using two DescLevels:
id = c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA, c4d.DTYPE_SUBCONTAINER, 0), c4d.DescLevel(1))
value = op.GetParameter(id, c4d.DESCFLAGS_GET_0)
But the Python API also allows you to do the same with less code:
value = op[c4d.ID_USERDATA, 1]
Or you access the user-data BaseContainer:
bc = op.GetData()
userDataValues = bc[c4d.ID_USERDATA]
value = userData[1]
The DescIDs are not necessarily stored in a file. The number of parameters of an object may change depending on how the object is used. The list of all displayed parameters is stored in the parameter Description. This Description contains the IDs as well as information on how to display the parameters in the Attribute Manager. The information how the parameter should be displayed is also stored as a BaseContainer (but this is NOT the object's BaseContainer).
The parameter description of user data parameters is stored in the object. You can access this data with GetUserDataContainer():
for id, descriptionBC in op.GetUserDataContainer():
print("User Data ID: " + str(id))
print("User Data Value: " + str(op[id]))
print("User Data Name: " + descriptionBC[c4d.DESC_NAME])
To make that clear: the VALUES of user data parameters are stored in the object's BaseContainer. The DESCRIPTION of the user data parameters is stored in the BaseContainers accessed with GetUserDataContainer()
.
You find an overview over the most fundamental concepts and classes currently in the C++ documentation under Foundations, especially Classic API Base Classes.
You can also find further information about these classes:
best wishes,
Sebastian