Solved Use existing data for DynamicDescription

Hello,
I was checking on how to add a new tab in the attributes manager using DynamicDescription object but I could only do so if I create all the data by myself. Is there any way that I can do this but from the data that already exist.
Example:
I have a tree-view where you can add different shaders and what I need is that whenever a shader is selected I need to show the attributes of that shader (created in a res file) in the Attributes Manager. Is such a thing possible?
Thank you.

Hello,

typically, you can extend the UI of a NodeData based plugin by implementing GetDDescription(). This allows to add new groups and parameters to that element's parameter description.

Within GetDDescription() you could access another object and use GetDescription() to load its description and copy it to your object's description.

It is also possible to connect these newly created parameters to the original object. This way you can interact with these parameters and edit the original object. Such a connection is created by implementing TranslateDescID().

This is a quickly thrown together example of a ObjectData plugin's GetDDescription() and TranslateDescID() that copies the parameters of the first object in the scene into its own parameter description:

Bool GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
{
  if (!description->LoadDescription(node->GetType())) return false;

  auto AddDescription = [](GeListNode *node, Description *description) -> maxon::Result<void>
  {
    BaseDocument* const doc = node->GetDocument();
    if(doc == nullptr)
      return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);

    BaseObject* const firstObject = doc->GetFirstObject();
    if(firstObject == nullptr)
      return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);

    // don't get description of yourself
    if(firstObject->IsInstanceOf(node->GetType()))
      return maxon::OK;

    // add dynamic group
    const DescID groupID = DescLevel(4000, DTYPE_GROUP, 0);
    BaseContainer settings = GetCustomDataTypeDefault(DTYPE_GROUP);
    settings.SetString(DESC_NAME, "Dynamic Group"_s);
    description->SetParameter(groupID, settings, 0);

    AutoAlloc<Description> sourceDescription;
    if(sourceDescription == nullptr)
      return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);

    // get the Description of the given object
    if(!firstObject->GetDescription(sourceDescription, DESCFLAGS_DESC::NONE))
      return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);

    void* handle = sourceDescription->BrowseInit();
    const BaseContainer* bc = nullptr;
    DescID id, gid;

    while (sourceDescription->GetNext(handle, &bc, id, gid))
    {
      // define new ID
      DescLevel topLevel = id[0];
      topLevel.id += 5000;

      DescID newID;
      newID.PushId(topLevel);
      
      const Int32 depth = id.GetDepth();
      for (Int32 i = 1; i < depth; ++i)
      {
        newID.PushId(id[i]);
      }

      // insert paramter into the dynamic group 
      description->SetParameter(newID, *bc, groupID);
    }

    sourceDescription->BrowseFree(handle);
    return maxon::OK;
  };

  iferr(AddDescription(node, description))
  {
    DiagnosticOutput("Error: @", err);
  }

  flags |= DESCFLAGS_DESC::LOADED;

  return SUPER::GetDDescription(node, description, flags);
}

Bool TranslateDescID(GeListNode *node, const DescID &id, DescID &res_id, C4DAtom *&res_at)
{
  if (!node)
    return false;

  if (id[0].id >= 5000)
  {
    BaseDocument* const doc = node->GetDocument();
    if (doc != nullptr)
    {
      BaseObject* const firstObject = doc->GetFirstObject();

      if (firstObject != nullptr)
      {
        res_at = (C4DAtom*)firstObject;

        // define new ID
        DescLevel topLevel = id[0];
        topLevel.id -= 5000;

        DescID newID;
        newID.PushId(topLevel);

        const Int32 depth = id.GetDepth();
        for (Int32 i = 1; i < depth; ++i)
        {
          newID.PushId(id[i]);
        }

        res_id = newID;
        return true;
      }
    }
  }

  return ObjectData::TranslateDescID(node, id, res_id, res_at);
}

best wishes,
Sebastian

Hello,

typically, you can extend the UI of a NodeData based plugin by implementing GetDDescription(). This allows to add new groups and parameters to that element's parameter description.

Within GetDDescription() you could access another object and use GetDescription() to load its description and copy it to your object's description.

It is also possible to connect these newly created parameters to the original object. This way you can interact with these parameters and edit the original object. Such a connection is created by implementing TranslateDescID().

This is a quickly thrown together example of a ObjectData plugin's GetDDescription() and TranslateDescID() that copies the parameters of the first object in the scene into its own parameter description:

Bool GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
{
  if (!description->LoadDescription(node->GetType())) return false;

  auto AddDescription = [](GeListNode *node, Description *description) -> maxon::Result<void>
  {
    BaseDocument* const doc = node->GetDocument();
    if(doc == nullptr)
      return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);

    BaseObject* const firstObject = doc->GetFirstObject();
    if(firstObject == nullptr)
      return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);

    // don't get description of yourself
    if(firstObject->IsInstanceOf(node->GetType()))
      return maxon::OK;

    // add dynamic group
    const DescID groupID = DescLevel(4000, DTYPE_GROUP, 0);
    BaseContainer settings = GetCustomDataTypeDefault(DTYPE_GROUP);
    settings.SetString(DESC_NAME, "Dynamic Group"_s);
    description->SetParameter(groupID, settings, 0);

    AutoAlloc<Description> sourceDescription;
    if(sourceDescription == nullptr)
      return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);

    // get the Description of the given object
    if(!firstObject->GetDescription(sourceDescription, DESCFLAGS_DESC::NONE))
      return maxon::UnexpectedError(MAXON_SOURCE_LOCATION);

    void* handle = sourceDescription->BrowseInit();
    const BaseContainer* bc = nullptr;
    DescID id, gid;

    while (sourceDescription->GetNext(handle, &bc, id, gid))
    {
      // define new ID
      DescLevel topLevel = id[0];
      topLevel.id += 5000;

      DescID newID;
      newID.PushId(topLevel);
      
      const Int32 depth = id.GetDepth();
      for (Int32 i = 1; i < depth; ++i)
      {
        newID.PushId(id[i]);
      }

      // insert paramter into the dynamic group 
      description->SetParameter(newID, *bc, groupID);
    }

    sourceDescription->BrowseFree(handle);
    return maxon::OK;
  };

  iferr(AddDescription(node, description))
  {
    DiagnosticOutput("Error: @", err);
  }

  flags |= DESCFLAGS_DESC::LOADED;

  return SUPER::GetDDescription(node, description, flags);
}

Bool TranslateDescID(GeListNode *node, const DescID &id, DescID &res_id, C4DAtom *&res_at)
{
  if (!node)
    return false;

  if (id[0].id >= 5000)
  {
    BaseDocument* const doc = node->GetDocument();
    if (doc != nullptr)
    {
      BaseObject* const firstObject = doc->GetFirstObject();

      if (firstObject != nullptr)
      {
        res_at = (C4DAtom*)firstObject;

        // define new ID
        DescLevel topLevel = id[0];
        topLevel.id -= 5000;

        DescID newID;
        newID.PushId(topLevel);

        const Int32 depth = id.GetDepth();
        for (Int32 i = 1; i < depth; ++i)
        {
          newID.PushId(id[i]);
        }

        res_id = newID;
        return true;
      }
    }
  }

  return ObjectData::TranslateDescID(node, id, res_id, res_at);
}

best wishes,
Sebastian

Thanks @s_bach
This is very helpful.
Best wishes.