custom metadata node



  • On 06/02/2018 at 02:52, xxxxxxxx wrote:

    Hi,
    Im pretty new with cinema4d but already have some experience in Maya.
    I need to embed some user data/metadata related to assets and scenes and i was wondering if i can create a custom empty node and define custom parameters to store anything i need.

    As i said im pretty new to the python api and cinema, so any help would be very useful.



  • On 06/02/2018 at 03:00, xxxxxxxx wrote:

    Ok, i think i can answer myself.

    In case anyone finds this useful, here is what i will do:

    I can create a Null Node and cinema4d has a "user data" prepared for adding custom parameters. That is perfectly suitable for what i need.

    Havent yet checked but i suppose i can lock and hide the node from render and also from the object properties tab.

    Regards.



  • On 06/02/2018 at 04:30, xxxxxxxx wrote:

    for adding metadata:

    def find_metadata_node() :
        root_objects = doc.GetObjects()
        metadata_nodes = [o for o in root_objects if o.GetName() == 'MY_METADATA_NODE']
        if metadata_nodes:
            if len(metadata_nodes) > 1:
                print 'more than 1 metadata node, make sure you are using the correct one'
            return metadata_nodes[0]
        return None

    def set_metadata( metadata='{}', metadata_node_name='MY_METADATA_NODE') :
        obj_node = find_metadata_node()
        if not obj_node:
            obj_node = create_custom_node(type=c4d.Onull, metadata_node_name=metadata_node_name)
        
        container = c4d.GetCustomDataTypeDefault(c4d.DTYPE_STRING)
        container[c4d.DESC_NAME] = 'Metadata'
        element = obj_node.AddUserData(container)
        obj_node[element] = metadata
        c4d.EventAdd()
        doc.InsertObject(obj_node)

    And finally for reading:

    def get_metadata(metadata_node_name='MY_METADATA_NODE') :
        obj_node = find_metadata_node()
        if not obj_node:
            return '{}' 
        data_id, data = obj_node.GetUserDataContainer()[0]
        metadata_str = obj_node[data_id]
        
        return metadata_str

    I insert metadata as a string. One thing i havent figured out yet is how to deal with multiple nodes because apparently, Cinema4d lets you create multiple null objects with exact same name without renaming or suffixing a number behind. For example one would expect to have 'MY_METADATA_NODE', 'MY_METADATA_NODE1', 'MY_METADATA_NODE2'.... How does cinema 4d handle repeated objects?



  • On 07/02/2018 at 09:02, xxxxxxxx wrote:

    Hi,

    welcome to the Plugin Café forums 🙂

    Cinema 4D actually doesn't care for the name of objects at all. There are a few special objects, where only the first enabled object in the scene has an effect (like e.g. background). You can use GetHighest() to find the effective object.

    Adding meta data to objects can be achieved in different ways:

    The technically best (or rather say recommended) way is to create a custom tag (may also be hidden from the user), which 
    carries the needed data and may or may not expose the data via a parameter description. See TagData plugins for this.
    Here's a Python example of a TagData plugin, even if it's doing a bit more than just adding data: Py-LookAtCamera.

    Probably the easiest way of adding data to an object (or a scene... or any other NodeData derived entity) is to simply store it in it's BaseContainer (see GetDataInstance()). For this make sure to get a unique ID here from the forum. Then store your own BaseContainer with this unique ID inside of the entity's BaseContainer (basically entitiesBc.SetContainer(uniqueId, yourCustomContainer) ). Within your own BaseContainer you can store whatever data you want. Of course in this simple scenario the data does not get exposed to the user.

    Your User Data approach is of course also an option. Really depends on your needs, what works best for you.

    Just for completeness sake, if you wanted to add data to a scene, the technically correct way would be via a SceneHook plugin, but that's not available in Python (link leads into C++ docs). So you would need to go the above described BaseContainer route.



  • On 08/02/2018 at 03:19, xxxxxxxx wrote:

    Thanks for the answer,
    my approach lacks an important fact for me, which is that the object should be hidden and locked from the user. 
    I havent found a way to do this yet. Is it possible? or should i use a custom tag approach?



  • On 08/02/2018 at 09:21, xxxxxxxx wrote:

    One option is described in this thread Hide object in the object manager, basically using ChangeNBit() with NBIT_OHIDE. But I wouldn't really recommend this, as it's not transparent to the user and also can not be undone by the user.

    I'd rather recommend to use layers. See functions GetLayerData(), SetLayerData() and SetLayerObject().
    In this way you have more control and it's also comprehensible for the user.



  • On 12/02/2018 at 04:32, xxxxxxxx wrote:

    Ok, here is what i finally did :

    call this to create a new layer

    c4d.CallCommand(100004738)

    iterate through the layers in root an find the newly created layer by name "Layer" and change layer name

    new_layer.SetName(layername)
    new_layer.SetBit(c4d.BIT_ACTIVE)
    new_layer[c4d.ID_LAYER_COLOR] = c4d.Vector(0.5,0.5,0.5)

    Retrieve modify and set layer data

    new_layer_data = new_layer.GetLayerData(c4d.documents.GetActiveDocument())
    new_layer_data['solo'] = False
    new_layer_data['view'] = False
    new_layer_data['render'] = False
    new_layer_data['manager'] = False
    new_layer_data['locked'] = True
    new_layer_data['generators'] = False
    new_layer_data['expressions'] = False
    new_layer_data['animation'] = False
    new_layer_data['deformers'] = False
    new_layer.SetLayerData(c4d.documents.GetActiveDocument(),new_layer_data)

    and finally assign layer to object

    obj = c4d.BaseObject(c4d.Onull)

    obj[c4d.ID_LAYER_LINK] = new_layer

    This way the object will be not visible in managers as well as locked through the layer



  • On 12/02/2018 at 08:02, xxxxxxxx wrote:

    Glad you found a solution.

    Just a suggestion: Instead of using CallCommand() to create a new layer and then trying to find it with the name, you can simply create a new layer directly.

        layerRoot = doc.GetLayerObjectRoot()
      
        new_layer = c4d.documents.LayerObject()
        new_layer.SetName("my layer")
      
        new_layer.InsertUnder(layerRoot)
    

Log in to reply