UNSOLVED ActiveObjectManager_SetObject to display BaseObject in a GetListHead

I am trying to figure out how to display ObjectData information when the object itself it is not directly in the Object Manager.

I have an ObjectData node that contains a GeListHead, which is parented to it. This object is actually in the scene and shows in the Object Manager list.

The GeListHead contains other ObjectData nodes.

I would like to be able to display the data for the child nodes of the GeListHead in the Attribute Manager. But I am finding that if I call ActiveObjectManager_SetObject then the nodes descriptions are only visable in the Attribute Manager until the next EventAdd is called, then it reverts back to whatever is selected in the Object Manager itself.

class MyObject : public ObjectData
{
public:
  virtual Bool Init(GeListNode *node)
  {
	_objects->SetParent(node);
  }

void ShowData()
{
	BaseObject* pObj = (BaseObject* )_objects->GetFirst();
	ActiveObjectManager_SetObject(ACTIVEOBJECTMODE::OBJECT, pObj, ACTIVEOBJECTMANAGER_SETOBJECTS_OPEN);
       EventAdd();
}

private:
  AutoAlloc<GeListHead> _objects;
};

Is ActiveObjectManager_SetObject able to be used this way?

Alternatively how does the Xpresso editor work? How does it ensure the selected Xpresso nodes data gets displayed in the Attribute Manager? Would it work if I used the same approach as above but instead used a TagData plugin that contains the GeListHead?

Thanks,
Kent

Hello @kbar,

Thank you for reaching out to us. This is a tricky one to answer, and my first instinct was that this is not too surprising due to what you do with your GeListHead, but the devil lies in the detail.

In general, I would say that the safest route would be the one many of our implementations take: Displaying elements that are not part of the "standardized" branches of a scene graph via a DescriptionCustomGui wrapped by a custom GUI of yours which is part of a description of a (set of) node type(s) you provide. The FieldList data type/GUI does this for example.

Overview

  1. Simply adding a custom branch in form of a GeListHead to a node of your choice will not make this custom branch discoverable via BranchInfo. This is because there is hard-coded data in the Cinema 4D core for this branch discovery. You can of course manually traverse the branches of your node.
  2. In the Attribute Manager SetObject implementation, this hardcoded branch structure is not being checked directly as far as I can see, but the Attribute Manger modes rely implicitly on it.
  3. You can register new Attribute Manger modes with ActiveObjectManager_RegisterMode which binds than that mode to a hook as for example shown in [1] (for materials in this case), see the MESSAGEHOOK documentation for details. It shows the case for tags as used internally. The object mode case is much more complex, but it will effectively fail on AOM_MSG_GETATOMLIST in your scene setup because the traversal done there assumes objects to appear in "object " branches only - which is not true in your case.

Solutions

  1. I personally would still consider the DescriptionCustomGui route the safest way, especially because the GUI has the DESCRIPTION_OBJECTSNOTINDOC flag which allows you to also display orphaned nodes.
  2. Implement your custom attribute manager mode, which can deal with your custom scene traversal structure and mark your nodes with NBIT_ACTIVE so that the hook can find them.
  3. Regarding Xpresso nodes: They have their own branches which are respected in the AM implementation.
  4. As a warning: There are also quite a few hacks in the AM for Nodes API and shader nodes, especially regarding the traversal. In general, I would consider this non-regular scene structure of yours a problem with the AM because of how often Cinema 4D internally relies on a specific makeup of branches. It could very well be that you implement your AM mode, and everything will then work fine, but I cannot guarantee that. Which is a further point why I personally would gravitate towards the first solution.

Cheers,
Ferdinand

[1]:

static GeData MaterialMessageHook(const BaseContainer &msg, void *data)
{
  switch (msg.GetId())
  {
    case AOM_MSG_ISENABLED:
      return true;

    case AOM_MSG_GETATOMLIST:
    {
      BaseDocument *doc = GetActiveDocument(); if (!doc) break;
      doc->GetActiveMaterials(*((AtomArray*)data));
      return true;
    }
    break;
  }
  return false;
}