HandleShaderPopupI() & BuildShaderPopupMenuI()



  • On 10/01/2017 at 07:25, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   13 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    Hi,
    I'm trying to detect which specific shader popup item was selected by the user in the "Textures" list, found in the color channel of my own MaterialData plugin.
    In other popup functions in the SDK. The way to detect the selected item is to use the mouse coords.
    But I don't know how to handle getting the mouse coords part in a MaterialData plugin.
    That kind of plugin does not list MouseInput(), Message(), or any of the other ways I've used to capture the mouse coords.

    *I'm assuming there is a way to simply handle the message sent when a specific item is selected?
    In that case I would not need the mouse coords. But I would still be left with the problem of not knowing how to handle that message without using Message() or CoreMessage(). Which don't seem to be supported in a MaterialData plugin.

    This is what I'm doing to get the ID#'s and name values for the popup list

        BaseMaterial *mat = doc->GetFirstMaterial();  
      if (!mat) return false;  
      
      //Get shader list  
      BaseContainer shaderList;  
      BuildShaderPopupMenuI(&shaderList, nullptr, nullptr, 0);  
      
      //Get the mouse's coords to see which popup item ("Texture") the cursor is over  
      LONG screenPosX = 0;  
      LONG screenPosY = 0;  
      LONG res = ShowPopupMenu(nullptr, screenPosX, screenPosX, shaderList);  
      
      //Handle result  
      BaseShader *shader = nullptr;  
      if (HandleShaderPopupI(mat, shader, res, 0) && shader)  
      {  
          GePrint("HandleShader");  
      
          //Get the ID#s and String values for all of the items in the shader's "Texture" popup list  
          LONG index = 0;  
          while (shaderList.GetIndexId(index) != NOTOK)  
          {  
              LONG id = shaderList.GetIndexId(index);  
      
              GeData data = shaderList.GetData(id);  
              GePrint("id String: " + LongToString(id) + " = " + data.GetString());  
       
              index++;  
          }          
      }  
    }
    

    -ScottA



  • On 11/01/2017 at 01:58, xxxxxxxx wrote:

    Hello,

    the function ShowPopupMenu() displays a popup menu. The given coordinates define where this popup menu is displayed, they have nothing to do with mouse coordinates or what item the user might select. The ID of the selected item is stored in the return value of the function.

    best wishes,
    Sebastian



  • On 11/01/2017 at 08:16, xxxxxxxx wrote:

    I'm afraid I don't follow you Sebastian.
    HandleShaderPopup(), BuildShaderPopupMenuI(), and ShowPopupMenu() all return Bools.
    I don't see any way to get the popup item the user clicked from them?

    In my MaterialData InitRender() method. I just want to catch which item was selected by the user in the popup list and print it to the console.

    I have figured out how to do it by getting the shader link like this.
    But I would like to know if we can do this same thing simply by getting the clicked popup item?

    INITRENDERRESULT ExampleMaterial::InitRender(BaseMaterial* mat, const InitRenderStruct& irs)  
    {  
      //This code block prints what kind of shader was added to the color channel by the user  
      const BaseDocument *const doc = mat->GetDocument();  
      GeData d;  
      BaseLink *colShaderLink = nullptr;  
      
      //COLOR_CHANNEL_SHADER is the Color channel's shader gizmo's ID I gave it in the .h file  
      if (mat->GetParameter(DescID(COLOR_CHANNEL_SHADER), d, DESCFLAGS_GET_0))  
      {          
          colShaderLink = d.GetBaseLink();  
          if (colShaderLink == nullptr) return INITRENDERRESULT_OK;  
          else  
          {  
              BaseShader *colShdr = static_cast<BaseShader*>(colShaderLink->GetLink(doc, Xbase));  
              GePrint(colShdr->GetTypeName());  
          }          
      }  
      
      return INITRENDERRESULT_OK;  
    }
    

    -ScottA



  • On 12/01/2017 at 00:07, xxxxxxxx wrote:

    Hello,

    ShowPopupMenu() does not return a Boolean value. It returns a Int32 value, storing the ID of the selected element.

    best wishes,
    Sebastian



  • On 12/01/2017 at 08:16, xxxxxxxx wrote:

    Oops. Sorry about that.
    I didn't mean to include ShowPopupMenu() in that Bool category.

    I'm having some difficulty making your example code work in my scenario. Because in your example ShowPopupMenu() is a new instance of the menu. But in my plugin I'm using the built-in menu. And I don't know how to get a pointer to that existing menu. And get what the user clicked.
    Using ShowPopupMenu() to get the existing menu seems wrong to me because I don't want to launch that menu with code. I just want to use the one that's already there.

    In short.
    Using the built-in popup menu in a MaterialData plugin requires different parameter values than the example code you posted. And I'm having difficulties figuring out.
    I'll keep on trying.

    Here's some code showing what I'm doing.

    INITRENDERRESULT ExampleMaterial::InitRender(BaseMaterial* mat, const InitRenderStruct& irs)  
    {  
      
      //Build the items of the popup menu here locally in code using the BuildShaderPopupMenuI()  
      BaseContainer shaderList;  
      BaseShader *shader = nullptr;  
      BuildShaderPopupMenuI(&shaderList, mat, shader, SHADERPOPUP_SETSHADER);  
      
      //Now lets loop through the shaderList container to get the popup item values   
      LONG index = 0;  
      while (shaderList.GetIndexId(index) != NOTOK)  
      {  
          LONG id = shaderList.GetIndexId(index);  
      
          GeData data = shaderList.GetData(id);  
          GePrint("id String: " + LongToString(id) + " = " + data.GetString());  
      
          index++;  
      }  
      
      //I don't want to build a new popup list, so I don't want to use ShowPopupMenu()  
      //I want to use the existing popup menu  
      //So I try to use it like this. But it doe not work        
      if (HandleShaderPopupI(mat, shader, 5832, 0)) //<--- If the color item was selected  
      {  
          GePrint("Color type shader was added");   //<---Does not work!   
      }  
      
      return INITRENDERRESULT_OK;  
    }
    

    If I must use the ShowPopupMenu() function. Then that's a problem, because I don't see any way of getting the mouse coords from inside of a MaterialData plugin.

    -ScottA

    *Edit-
    The more time I spend on this. The more I'm thinking that HandleShaderPopupI() only works with a custom built menu. And does not work with the existing ones.
    I have no idea if HandleShaderPopup() is any different. Because I can't get that one to work either.



  • On 13/01/2017 at 00:40, xxxxxxxx wrote:

    Hello,

    BuildShaderPopupMenuI() fills a BaseContainer with the shader list, ShowPopupMenu() displays that list as a popup menu, HandleShaderPopupI() takes the result of the interaction with that menu and provides access to the associated shader. This has nothing to do with MaterialData plugins.

    What do you mean with "built-in popup menu"? Are you talking about a shader-link parameter? If you want to check if such a parameter was changed you have to implement SetDParameter().

    best wishes,
    Sebastian



  • On 13/01/2017 at 07:54, xxxxxxxx wrote:

    Yes.
    The more time I spent on this, the more I realized that these functions in question were for a special case usage for generating the shader popup list. And we must use them together as a group. Meaning that we must use ShowPopupMenu() with them. Which is not what I want to do.
    I just wanted to handle the click event message that occurs on the existing MaterialData shader link popup list.

    I'll try using the SetDParameter() next. But that method is not listed as a supported overridden method in the docs for this kind of plugin. So I assumed it was not even an option.
    If it works, then it must work through inheritance.

    I'll give it try.
    Thanks,
    -ScottA

    Update:
    As I feared. According to my notes and the doc. SetDParameter() is only for node based plugins. And does not work in MaterialData plugins.
    When Maxon wrote their Materials code. Someone at the company used the ShowPopupMenu() method to make the shader pop list open when the little arrow button is pressed by the user.
    What I need is the variable name they assigned to it. Then I can simply get the Long result from it to know which item the user selected.
    Is there any way for us to get the value of that variable?



  • On 16/01/2017 at 00:54, xxxxxxxx wrote:

    Hello,

    MaterialData is based on NodeData so you can implement SetDParameter().

    best wishes,
    Sebastian



  • On 16/01/2017 at 07:58, xxxxxxxx wrote:

    Getting closer. But still not quite there.

    Bool ExampleMaterial::SetDParameter(GeListNode *node, const DescID &id, const GeData &t_data, DESCFLAGS_SET &flags)  
    {  
      if (!node) return false;  
      
      //GePrint(LongToString(id[0].id));   //Prints 2003 when any of the shader popup list items are selected  
      
      switch (id[0].id)  
      {  
          case 2003:  
          {              
              LONG value = t_data.GetLong();  
              GePrint(LongToString(value));   //<---Always returns 0  
      
              //How do I get the item the user clicked???  
      
              //Mark the parameter as set  
              flags |= DESCFLAGS_SET_PARAM_SET;  
      
              //Return from the function, don't call the function of the base class.  
              return true;              
          }break;  
      }  
      return SUPER::SetDParameter(node, id, t_data, flags);  
    }
    

    -ScottA



  • On 16/01/2017 at 23:48, xxxxxxxx wrote:

    Hello,

    a shader-link parameter stores a BaseLink to a BaseShader.

      
    BaseLink* link = t_data.GetBaseLink();  
    if (link)  
    {  
      BaseList2D* shader = link->ForceGetLink();  
      if (shader)  
      GePrint(shader->GetName());  
    }  
    

    best wishes,
    Sebastian



  • On 17/01/2017 at 07:34, xxxxxxxx wrote:

    Thanks. ForceGetLink() was the thing I was missing.
    But isn't this code doing the same thing as I posted in Post#3. Only with slightly different code?

    In this code, and the code in post#3. The code is getting the shader object that resides in the link.
    That object gets created based on what popup item the user clicked.
    But what I was originally wondering how to do is get the popup item's name(or ID#) when one of the popup items is selected. Before it actually creates anything.
    Not getting the item clicked by getting the shader object it created.

    In the code you posted in the other thread. We can get that value from res (the variable assigned to ShowPopupMenu()).
    But I'm still wondering if/how we can do the same thing with the existing popup shader menu?
    Is that possible?

    -ScottA



  • On 18/01/2017 at 00:29, xxxxxxxx wrote:

    Hello,

    you cannot access the "existing popup shader menu". You MaterialData plugin may include a shader-link parameter. This parameter is presented in the Attribute Manager or Material Manger with some GUI. This GUI internally uses that Pop-Up menu, but only for internal purposes to set the shader. One might create a custom GUI for shader-link parameters that works differently. A material should typically not care about how the parameter is set but to what value it is set.

    best wishes,
    Sebastian



  • On 18/01/2017 at 07:23, xxxxxxxx wrote:

    OK. Thanks.
    I have a much better understanding of what's happening with that popup menu now.
    And the ForceGetLink() code was something missing from my notes too.

    Here's my code in case anyone needs it:

    Bool MyMaterial::SetDParameter(GeListNode *node, const DescID &id, const GeData &t_data, DESCFLAGS_SET &flags)  
    {  
      if (!node) return false;  
      
      switch (id[0].id)  
      {  
          //2003 is the ID sent when the shader popup list pops up  
          case 2003:  
          {  
              BaseLink *link = t_data.GetBaseLink();  
              if (link)  
              {  
                  BaseList2D* shader = link->ForceGetLink();  
                  if (shader) GePrint(shader->GetName());  
              }              
                
              //Use this only if you want to override the shader being created to something else  
              //For the default shader popup list this would not make much sense  
              //flags |= DESCFLAGS_SET_PARAM_SET;  
       
              return true;              
          }break;  
      }  
      return SUPER::SetDParameter(node, id, t_data, flags);  
    }
    

    -ScottA


Log in to reply