node interface similar to xpresso



  • On 29/12/2014 at 19:24, xxxxxxxx wrote:

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

    ---------
    I'm trying to create a node based system GUI, here is an old thread: https://plugincafe.maxon.net/topic/8083/10517_creating-a-custom-node-system&KW=

    last night I found this video: https://vimeo.com/115522832

    which is a custom node system based on xpresso GUI

    how to do something similar to this? "would cut the effort of creating a whole node GUI while xpresso GUI is already made!!"



  • On 30/12/2014 at 05:04, xxxxxxxx wrote:

    Hello,

    you can create your own Xpresso Nodes based on GvOperatorData. To display a node system you can create a GeDialog that has a GvNodeGUI element (allocated with AllocNodeGUI()).

    A node system is stored in a GvNodeMaster object. This GvNodeMaster must be handled in a host object.

    Best wishes,
    Sebastian



  • On 30/12/2014 at 09:35, xxxxxxxx wrote:

    Hi Sebastian,

    thanks for the reply, I kinda understand the whole picture now, a few questions:
    1- how the data is processed? "in a compile way similar to LLVM, or a function pointer?"
    2- I don't want the calculation to be immediate, want it to happen only if an event occurs "like a command plugin event" , how can I achieve this?

    edit:
    3- can I force a port color? "like float data port = red, int data port = green, and so on..."



  • On 31/12/2014 at 09:12, xxxxxxxx wrote:

    Hello,

    the calculation of a node is done in it's Calculate() method. This has nothing to do with LLVM or function pointers.

    So if you just want to use the above classes to create some user interface you don't have to do any calculation in Xpresso. You can just use the node system as a basis for your own calculations when you want to to this.

    The color of a port cannot be changed.

    Best wishes,
    Sebastian



  • On 31/12/2014 at 14:45, xxxxxxxx wrote:

    thanks a lot Sebastian , and happy new year 🙂



  • On 07/01/2015 at 15:12, xxxxxxxx wrote:

    Can some please post an example  of using the GvNodeGUI element to create a custom xpresso window in a GeDialog plugin?

    Nothing too fancy or elaborate. Just something quick and dirty without error checking to help me understand the basics of creating the master window. An how to add a node to it.
    I think I can use the SDK from there if I see how write the basic framework.

    Thanks,
    -ScottA



  • On 08/01/2015 at 07:10, xxxxxxxx wrote:

    Hello,

    a GvNodeGUI returns a GeUserArea that has to be added to the host dialog. A simple example can look like this:

      
    virtual Bool CreateLayout()  
    {  
     SetTitle("My Dialog");  
      
     if(_nodeGUI == nullptr)  
     {  
         _shape= GvGetWorld()->AllocShape();  
         _group= GvGetWorld()->AllocGroupShape();  
      
         _nodeGUI = GvGetWorld()->AllocNodeGUI(_shape,_group,1000);  
     }  
      
     if(_nodeMaster && _nodeGUI)  
     {  
         _nodeGUI->Attach(this,_nodeMaster);  
      
         AddUserArea(1000, BFH_SCALEFIT | BFV_SCALEFIT);  
         AttachUserArea(*_nodeGUI->GetUserArea(), 1000, USERAREA_TABSTOP|USERAREA_HANDLEFOCUS);  
     }  
      
     return true;  
    };  
    

    where _nodeGUI is the GVNodeGUI and _nodeMaster is the GvNodeMaster.

    Best wishes,
    Sebastian



  • On 08/01/2015 at 08:53, xxxxxxxx wrote:

    Thank you Sebastian. But that's not quite enough information.
    I still don't understand how to create _nodeGUI and _nodeMaster.

    Here is my entire working GeDialog plugin with a User Area attached to it.
    I'm trying to figure out how to use the UA as an xpresso window. But I'm having trouble understanding how to set up the basic framework for it.

    #include "c4d.h"  
    #include "c4d_symbols.h"  
    #include "ge_dynamicarray.h"  
      
    #define PLUGIN_ID 1000006  // be sure to use a unique ID obtained from www.plugincafe.com  
      
    enum  
    {  
      USERAREA = 3001  
    };  
      
    class MyUserArea : public GeUserArea  
    {  
      //For the mouse positions within the User Area while LMB dragging  
      LONG mouseX;  
      LONG mouseY;  
      LONG prevX;  
      LONG prevY;  
      
      public:  
          MyUserArea(void);  
          virtual Bool Init(void);  
          virtual Bool GetMinSize(LONG &w,LONG &h);  
          virtual void DrawMsg(LONG x1,LONG y1,LONG x2,LONG y2, const BaseContainer &msg);  
          virtual Bool InputEvent(const BaseContainer& msg);  
          virtual LONG Message(const BaseContainer &msg, BaseContainer &result);  
    };  
      
    MyUserArea::MyUserArea(void)  
    {}  
      
    Bool MyUserArea::Init(void)  
    {  
      return TRUE;  
    }  
      
    Bool MyUserArea::GetMinSize(LONG &w,LONG &h)  
    {  
      w = 200;  
      h = 200;  
      return TRUE;  
    }  
      
    void MyUserArea::DrawMsg(LONG x1,LONG y1,LONG x2,LONG y2, const BaseContainer &msg)  
    {  
      //To make the bitmaps more stable. Use OffScreenOn() before drawning them in GUI's  
      OffScreenOn();  
      DrawSetPen(COLOR_TEXT);          //Set a color for the UA  
      DrawRectangle(x1, y1, x2, y2);   //Draws the rectangle UserArea using the color  
    }  
      
    Bool MyUserArea::InputEvent(const BaseContainer& msg)  
    {  
      LONG dev = msg.GetLong(BFM_INPUT_DEVICE);     //Get the device being used (mouse, keyboard, etc...)  
      LONG chn = msg.GetLong(BFM_INPUT_CHANNEL);    //Get the device's component (LMB, MWheel, keyboard key, etc...)  
      
      if (dev==BFM_INPUT_MOUSE)  
      {  
          //Create an action message and also get the UserArea's id. And store them in a container called action  
          //This will be used later to tell the parent dialog that the UA has been changed in some manner  
          BaseContainer action(BFM_ACTION);  
          action.SetLong(BFM_ACTION_ID, GetId());  
          action.SetLong(BFM_ACTION_VALUE, 0);  
      
          //If the left mouse button is down...do some desired action  
          if(chn==BFM_INPUT_MOUSELEFT)  
          {   
              LONG mx = msg.GetLong(BFM_INPUT_X);            //Get the mouses's X position  
              LONG my = msg.GetLong(BFM_INPUT_Y);            //Get the mouses's Y position  
              Bool dc = msg.GetBool(BFM_INPUT_DOUBLECLICK);  //Checks if the LMB was double clicked or not  
      
              Global2Local(&mx,&my);                       //Converts the mouse positions to screen values relative to the UA                  
      
              BaseContainer mState;                        //Create a container that we will store the mouse actions we do in this next loop  
              while (GetInputState(BFM_INPUT_MOUSE,BFM_INPUT_MOUSELEFT, mState))  
              {  
                  if (mState.GetLong(BFM_INPUT_VALUE)==0) break;  //If the state value is 0. The LMB is no longer down..so exit the loop  
      
                  LONG dx = mState.GetLong(BFM_INPUT_X);        //Store the mouse positions only while dragging it  
                  LONG dy = mState.GetLong(BFM_INPUT_Y);        //Store the mouse positions only while dragging it  
                  Global2Local(&dx,&dy);  
      
                  //We can't use any Draw functions in the  InputEvent() method. We have to do it in the DrawMsg() method  
                  //So we must pass the mouse coords to class level variables so the DrawMsg() method can use them  
                  prevX=dx;  
                  prevY=dy;  
                  mouseX=dx;  
                  mouseY=dy;  
      
                  GePrint("X Pos: " +LongToString(dx) + "   " +  "Y Pos: " +LongToString(dy));  
                  Redraw();  
                  //action.SetLong(BFM_ACTION_INDRAG,TRUE);  
                  //SendParentMessage(action);  
              }  
              Redraw();  
      
              //Notify the parent dialog that the dragging is now finished  
              action.SetLong(BFM_ACTION_INDRAG, FALSE);  
              SendParentMessage(action);  
          }  
      }  
      return TRUE;  
    }  
      
    LONG MyUserArea::Message(const BaseContainer &msg, BaseContainer &result)  
    {  
      switch (msg.GetId())  
      {  
          //This code block returns the mouse's position in the UserArea without the LMB being pressed  
          case BFM_GETCURSORINFO:  
          {  
              LONG mouseX = msg.GetLong(BFM_DRAG_SCREENX);  
              LONG mouseY = msg.GetLong(BFM_DRAG_SCREENY);  
              if (Screen2Local(&mouseX, &mouseY))  GePrint(LongToString(mouseX) + ", " + LongToString(mouseY));  
          }  
          break;    
      
      }  
      
      return GeUserArea::Message(msg, result);  
    }  
      
      
    enum  
    {  
      MY_TEXT = 1001  
    };  
    class MyDialog : public GeDialog  
    {  
      private:  
         MyDialog *dlg;  
         MyUserArea ua;        //Create an instance of the MyUserArea class to use in this dialog's class  
         C4DGadget *ua_gui;    //Create a gizmo variable here so we can use it in all methods. Not just in CreateLayout()  
      
      public:  
          MyDialog(void);  
          ~MyDialog(void);  
          virtual Bool CreateLayout(void);  
          virtual Bool InitValues(void);  
          virtual Bool Command(LONG id,const BaseContainer &msg);  
          virtual LONG Message(const BaseContainer &msg,BaseContainer &result);  
      
    };  
      
    MyDialog::MyDialog(void)  
    {      
    }  
      
    MyDialog::~MyDialog(void)  
    {      
      GeFree(dlg);  
      GeFree(ua);  
    }  
      
    Bool MyDialog::CreateLayout(void)  
    {  
      Bool res = TRUE;  
      res = LoadDialogResource(IDS_RESDIALOG,NULL,0);  
      
      //This text box we'll add here in this .cpp file. And not from the external .res file  
      AddStaticText(MY_TEXT, BFH_SCALEFIT, 0,0, "my text", 0);  
      
      
      //Trying to create an Xpresso based UserArea...Not working!!!  
      if(_nodeGUI == nullptr)  
      {  
          _shape = GvGetWorld()->AllocShape();  
          _group = GvGetWorld()->AllocGroupShape();  
      
          _nodeGUI = GvGetWorld()->AllocNodeGUI(_shape,_group,1000);  
      }  
      
      if(_nodeMaster && _nodeGUI)  
      {  
          _nodeGUI->Attach(this,_nodeMaster);         
      
          ua_gui = AddUserArea(USERAREA, BFH_SCALEFIT | BFV_SCALEFIT);  
          //if(ua_gui) AttachUserArea(ua, ua_gui);  <---this works fine  
          if(ua_gui) AttachUserArea(*_nodeGUI->GetUserArea(), 1000, USERAREA_TABSTOP|USERAREA_HANDLEFOCUS);  
      }  
      
      return res;  
    }  
      
    Bool MyDialog::InitValues(void)  
    {  
      //first call the parent instance  
      if (!GeDialog::InitValues()) return FALSE;      
      
      this->SetString(MY_TEXT,"Xpresso Node Example");  
      
      return TRUE;  
    }  
      
    Bool MyDialog::Command(LONG id,const BaseContainer &msg)  //This is where the code that does something goes  
    {  
      BaseDocument *doc = GetActiveDocument(); //Get the active document  
      
      //Set up some actions that will tell c4d that a gizmo has been triggered..We'll used those action variables later on in the switch code block  
      LONG myBtn = msg.GetLong(BFM_ACTION_VALUE);    //Assigns an action to a variable  
      GePrint(LongToString(myBtn));  
      
      EventAdd();  
      return TRUE;  
    }  
      
    LONG MyDialog::Message(const BaseContainer &msg, BaseContainer &result)  
    {  
      switch(msg.GetId())  
       {  
         case BFM_INPUT:   //A dialog/userarea receives this message if any mouse or keyboard input is received  
      
            if(msg.GetLong(BFM_INPUT_DEVICE) == BFM_INPUT_KEYBOARD) //If the input is from the keyboard  
             {  
               String input = msg.GetString(BFM_INPUT_ASC);     //Create a string type variable...   
               GePrint(input);                                  //and assign it to the pressed key's unicode-text value  
             }  
      
            break;  
       }  //End the key pressed case loop /////////////////////////  
      
       return GeDialog::Message(msg,result);  
    }  
      
    class XnodeDialog : public CommandData  
    {  
      private:  
          MyDialog dlg;  
      public:  
          virtual Bool Execute(BaseDocument *doc);  
          virtual Bool RestoreLayout(void *secret);          
    };  
      
    Bool XnodeDialog::Execute(BaseDocument *doc)  
    {  
      StopAllThreads();  
      return dlg.Open(DLG_TYPE_ASYNC,PLUGIN_ID, -1, -1, 300,150);  
    }  
      
    Bool XnodeDialog::RestoreLayout(void *secret)  
    {  
      return dlg.RestoreLayout(PLUGIN_ID,0,secret);  
    }  
      
    Bool RegisterXnodeDialog(void)  
    {           
      String Help = "Status bar text here...";  
      //Register the plugin  
      return RegisterCommandPlugin(PLUGIN_ID, "XNode Dialog Example", 0, AutoBitmap("icon.tif"),Help, gNew XnodeDialog);  
    }
    

    -ScottA



  • On 08/01/2015 at 09:27, xxxxxxxx wrote:

    Hello,

    as you can see in the code example _nodeGUI is created using AllocNodeGUI(). A GvNodeMaster can be created using AllocNodeMaster(). Both functions are part of GvWorld which can be obtained using GvGetWorld(). Make sure to properly manage the GvNodeMaster in a parent object by handling its creation, destruction and functions like ReadObject(), WriteObject() and CopyTo().

    Best wishes,
    Sebastian



  • On 08/01/2015 at 10:04, xxxxxxxx wrote:

    Oh I think I see.
    I don't need to create a UA gizmo myself. The GvGetWorld()->AllocNodeGUI() creates it?

    I added these class member variables to try to make your code work.
         GvShape *_shape;
         GvShape *_group;
         GvNodeGUI *_nodeGUI;

    But I don't know who should be the master ("_nodeMaster") holding the GvNodeGUI?
    Do I have to use a BaseObject as the master or can it be the dialog?

    It's all very confusing without some simple example to see who goes where. And who does what.

    -ScottA



  • On 08/01/2015 at 12:02, xxxxxxxx wrote:

    Made some progress...I think.🙂
    But it crashes when the owner object is deleted. Or if I close C4D. Ouch!

    #include "c4d.h"  
    #include "c4d_symbols.h"  
    #include "ge_dynamicarray.h"  
    #include "c4d_graphview.h"  
      
    #define PLUGIN_ID 1000006  // be sure to use a unique ID obtained from www.plugincafe.com  
      
    enum  
    {  
      MY_TEXT = 1001  
    };  
    class MyDialog : public GeDialog  
    {  
      private:  
         MyDialog *dlg;  
      
         //xpresso node stuff  
         GvShape *_shape;  
         GvShape *_group;  
         GvNodeGUI *_nodeGUI;  
         GvNodeMaster *_nodeMaster;  
      
      public:  
          MyDialog(void);  
          ~MyDialog(void);  
          virtual Bool CreateLayout(void);  
          virtual Bool InitValues(void);  
          virtual Bool Command(LONG id,const BaseContainer &msg);  
          virtual LONG Message(const BaseContainer &msg,BaseContainer &result);  
      
    };  
      
    MyDialog::MyDialog(void)  
    {}  
      
    MyDialog::~MyDialog(void)  
    {      
      GeFree(dlg);  
      GvGetWorld()->FreeShape(_shape);  
      GvGetWorld()->FreeShape(_group);  
      GvGetWorld()->FreeNodeGUI(_nodeGUI);  
      GvGetWorld()->FreeNodeMaster(_nodeMaster);  //<---Not sure if this is freeing them properly?    
    }  
      
    Bool MyDialog::CreateLayout(void)  
    {  
      Bool res = TRUE;  
      res = LoadDialogResource(IDS_RESDIALOG,NULL,0);  
      
      //This text box we'll add here in this .cpp file. And not from the external .res file  
      AddStaticText(MY_TEXT, BFH_SCALEFIT, 0,0, "my text", 0);  
      
      
      //Create an Xpresso based UserArea  
      if(_nodeGUI == nullptr)  
      {  
          GvShape *_shape = GvGetWorld()->AllocShape();  
          GvShape *_group = GvGetWorld()->AllocGroupShape();  
          _nodeGUI = GvGetWorld()->AllocNodeGUI(_shape,_group,1000);  
      }  
      
      GvNodeMaster *_nodeMaster = GvGetWorld()->AllocNodeMaster(GetActiveDocument()->GetFirstObject(), TRUE, TRUE);  //Allocates a node master. Must be freed with FreeNodeMaster().  
      if(_nodeMaster && _nodeGUI)  
      {  
          _nodeGUI->Attach(this,_nodeMaster);  
          if(_nodeGUI) AttachUserArea(*_nodeGUI->GetUserArea(), 1000, USERAREA_TABSTOP|USERAREA_HANDLEFOCUS);  
      }  
      
      return res;  
    }  
      
    Bool MyDialog::InitValues(void)  
    {  
      //first call the parent instance  
      if (!GeDialog::InitValues()) return FALSE;      
      
      this->SetString(MY_TEXT,"Xpresso Node Example");  
      
      return TRUE;  
    }  
      
    Bool MyDialog::Command(LONG id,const BaseContainer &msg)  //This is where the code that does something goes  
    {  
      BaseDocument *doc = GetActiveDocument(); //Get the active document  
      
      //Set up some actions that will tell c4d that a gizmo has been triggered..We'll used those action variables later on in the switch code block  
      LONG myBtn = msg.GetLong(BFM_ACTION_VALUE);    //Assigns an action to a variable  
      GePrint(LongToString(myBtn));  
      
      EventAdd();  
      return TRUE;  
    }  
      
    LONG MyDialog::Message(const BaseContainer &msg, BaseContainer &result)  
    {  
      switch(msg.GetId())  
       {  
         case BFM_INPUT:   //A dialog/userarea receives this message if any mouse or keyboard input is received  
      
            if(msg.GetLong(BFM_INPUT_DEVICE) == BFM_INPUT_KEYBOARD) //If the input is from the keyboard  
             {  
               String input = msg.GetString(BFM_INPUT_ASC);     //Create a string type variable...   
               GePrint(input);                                  //and assign it to the pressed key's unicode-text value  
             }  
      
            break;  
       }  //End the key pressed case loop /////////////////////////  
      
       return GeDialog::Message(msg,result);  
    }  
      
    class XnodeDialog : public CommandData  
    {  
      private:  
          MyDialog dlg;  
      public:  
          virtual Bool Execute(BaseDocument *doc);  
          virtual Bool RestoreLayout(void *secret);          
    };  
      
    Bool XnodeDialog::Execute(BaseDocument *doc)  
    {  
      StopAllThreads();  
      return dlg.Open(DLG_TYPE_ASYNC,PLUGIN_ID, -1, -1, 300,150);  
    }  
      
    Bool XnodeDialog::RestoreLayout(void *secret)  
    {  
      return dlg.RestoreLayout(PLUGIN_ID,0,secret);  
    }  
      
    Bool RegisterXnodeDialog(void)  
    {           
      String Help = "Status bar text here...";  
      //Register the plugin  
      return RegisterCommandPlugin(PLUGIN_ID, "XNode Dialog Example", 0, AutoBitmap("icon.tif"),Help, gNew XnodeDialog);  
    }
    

    -ScottA



  • On 09/01/2015 at 05:28, xxxxxxxx wrote:

    Hello,

    _nodeMaster  is a GvNodeMaster object that stores a node system. The GvNodeGUI is just a GUI to display that node system.

    The GvNodeMaster object must be saved somewhere. Typically it is a member of a parent object, like the Xpresso tag that hosts a node system. When you call AllocNodeMaster() you must define that parent object.

    This parent object must make sure that the GvNodeMaster object is handled correctly, that it is created and properly deleted and that it's content is written into a file and written to a file using ReadObject() and WriteObject() .

    Best wishes,
    Sebastian



  • On 09/01/2015 at 07:22, xxxxxxxx wrote:

    I tried assigning _nodeMaster to an xpresso tag. But it didn't work.
    Also _nodeGUI is not producing anything in my GeDialog.

    I think I know what all the parts are supposed to do.
    I've written some xpresso code before.
    But I can't figure out how to create the GUI in my GeDialog.

    -ScottA



  • On 12/01/2015 at 01:05, xxxxxxxx wrote:

    Hello,

    you cannot "assign" a GvNodeMaster object to an existing object-intsance. As said before, it should be a member of an object so for example your own plugin object, material or tag class.

    best wishes,
    Sebastian



  • On 12/01/2015 at 07:15, xxxxxxxx wrote:

    Thanks for trying Sebastian.

    Even though I can't figure out how to put the xpresso window in my dialog. I was able to launch one from a button in my dialog. And when I do that I the nodes don't return any values. And I'm also getting memory leaks from the nodes I add with code.
    They also get deleted when the dialog is closed. But I'm guessing that happens because I don't have the Read(),Write(), CopyTo() code in place yet.

    At this pace it would take me a year for me to figure this all out.
    I really need to see a working example. So I'm giving up on this for now.
    All I can do is ask to please consider adding an example of this to the SDK in the future.

    Thanks for trying,
    -ScottA


Log in to reply