BaseContainer and UserData
What is the proper way to initialize user data in a BaseContainer?
bc->SetData( paramId, GeData( MY_CUSTOMDATATYPE, DEFAULTVALUE ) );
auto userDataContainer = bc->GetContainerInstance( ID_USERDATA ); userDataContainer->SetData( paramId, GeData( MY_CUSTOMDATATYPE, DEFAULTVALUE ) );
I always used the former, but noticed a BaseContainder inside
NodeData::Read()formatted like the latter.
ferdinand last edited by ferdinand
thank you for reaching out to us. We are a bit confused by your question, because your two code snippets either do different things or it is at least is not obvious what you are doing in the first example. So your question of which one is the better solution, cannot be answered.
In the first example of yours, you write to the ID
BaseContainer; from the context it seems reasonable to assume that this container is meant to be either a copy or an instance of a node's data container. But since you write a non-container value, your custom data type, the ID
paramIdcannot be the ID
ID_USERDATA, the id where the user data container is stored in a node's data container. In your second example you do retrieve that user data container from the node's data container first and then do the write operation of your custom data into the user data container.
So you are doing two different things here, in the first case you write to some id in the data container and in the second case you write to some id in the user data container (which is attached to the data container). Your first example should not be parsed by Cinema 4D as user data and subsequently not show up in the user data UI. The only place where
paramIdcould show up is in the UI of the node, if said ID part of the description of that node.
I hope this makes sense,
Yes, both do different things, but because I want to understand which one is correct, if any, when initializing the object inside
My custom user data does not have UI and is not available to be added manually on the object's UserData menu, so it didn't pass in my mind to look there. I declare them inside
ID_OBJECTPROPERTIESof the res file.
I rarely access data directly from the node's data container, other than in
NodeData::Init(). I usually use
GetParameter(), with the proper DescId levels. From your answer I understand that I will be retrieving the second snippet's data later with
If I do not initialize at all, peeking at the data container after I use
SetParameter(), user data are inside a 700 container.
ferdinand last edited by ferdinand
I am even more confused now by your answer :) So let me explain what I was talking about. I will refer to your first example as
f1and to your second example as
f2. I will assume that
1and for simplicity I will also assume a
DTYPE_LONG(i.e. an integer value) instead of your custom data type. Let's assume that we write the value
After you have run
f1, the data container of the node will look like this:
// f1 wrote simply to the index `1` of the container, here the value `42`. // This is dangerous, because Cinema stores internal data in the data container range 0 to 1000. // You should never write to this range. I assumed value `1` for `paramID`, because user data IDs // usually start in this very low range. : 42 ... // The user data container, it will be always in data container. [700, i.e. ID_USERDATA]: BaseContainer // In this case we assume the user data container to be empty ...
And after you have run
f2, the data container of the node will look like this:
... // The user data container, it will be always in data container. [700, i.e. ID_USERDATA]: BaseContainer // The value written by `f2` : 42 ...
Both values can be retrieved with
GetParamaterwhich is mostly a convenience wrapper around the data container of a node. The differences for
- They enforce the data type of the container at the given id, i.e. you cannot simply overwrite an existing value type with another value type.
- They allow for the access of node attributes which are not being stored in its data container, e.g. its name.
- They allow to reach "deeper" into a data container which can contain composed value types (e.g. a
Vector) . This is done via
To retrieve the value written by
f1, we simply need a
paramIdwas not a container type value.
const DescLevel dlvlParamId (paramId, DTYPE_LONG, 0); const DescID did = DescID(dlvlParamId);
DescIDwould have to have two levels. This would be an example for reaching "deeper", here retrieving a
dlvlParamIdwould be of
DTYPE_VECTOR, we could also reach one more level further down, e.g., we could retrieve the first component of a vector inside a subcontainer.
const DescLevel dlvlUserData(ID_USERDATA, DTYPE_SUBCONTAINER, 0); const DescLevel dlvlParamId(paramId, DTYPE_LONG, 0); const DescID did = DescID(dlvlUserData, dlvlParamId);
So, I still don't quite get what your goals are, but yes, initializing an attribute in a data container will make it accessible as a parameter. But you do not clarify how the (dynamic) description of the data container hosting node is constituted, so there might be no GUI for your data. The "proper" way to create a new user data entry is described in the DynamicDescription Manual. Which then later can be written to in the way described here.
I hope this helps and happy holidays,
Thanks, @zipit that's very clarifying.