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://developers.maxon.net/forum/topic/8083/10517_creating-a-custom-node-system&KW=last night I found this video:
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
[URL-REMOVED]. To display a node system you can create a GeDialog that has aGvNodeGUI
[URL-REMOVED] element (allocated withAllocNodeGUI()
[URL-REMOVED]).A node system is stored in a
GvNodeMaster
[URL-REMOVED] object. This GvNodeMaster must be handled in a host object.Best wishes,
Sebastian
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
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()
[URL-REMOVED] 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
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
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()
[URL-REMOVED]. A GvNodeMaster can be created usingAllocNodeMaster()
[URL-REMOVED]. Both functions are part of GvWorld which can be obtained usingGvGetWorld()
[URL-REMOVED]. 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
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
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
[URL-REMOVED] 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()
[URL-REMOVED] 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()
[URL-REMOVED] andWriteObject()
[URL-REMOVED] .Best wishes,
Sebastian
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
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