Implement 'Set Driven Keys' in C++?

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

THREAD CLOSED
 
Thread Tools Search this Thread Display Modes
Old 03 March 2006   #1
Implement 'Set Driven Keys' in C++?

Remember that I said that one important part of interPoser Pro would be implementing Master/Slave dials. Well, after some basic tests, "Set Driven Keys" is exactly the best approach - allows user editing, uses existing Cinema 4D functionality, easily loaded and saved.

The problem is that this feature is what I call a 'composite' function. Cinema 4D seems to be filled with 'composite' functions - commands that group a set of individual tasks into a single or couple of actions. Set Driven Keys does just that - it gets the 'driver' object and 'driven' object and then adds the XPresso tag and nodes and configures so that the driver parameter drives the driven parameter. The problem with many composite functions is that they are inaccessible from the C++ SDK - especially if it's a right-context menu item in the Attribute Manager - oye.

I know nothing about GraphView - and boy is it a complex interface. I swear that half of the SDK Docs are just about this. Nothing of use at PluginCafe (mainly about implementing custom GvNodes). No examples. No code snippets in the docs (that I've located).

Anyone have any information about how one would go about 'reinventing the Set Driven Keys' programmatically? So far, I have gotten to creating the nodes and attempting to add ports and connections (IsConnectionValid() is complete gibberish without more information).

It is expected to be pulling-teeth time. My pliers are handy...

Robert
__________________
Greebler | InterPoser

C4D-R16, VS2012, Xcode 4.6
 
Old 03 March 2006   #2
doesn't seem that complex to me looking at the docs, though i've not tried it.

but i would allocate an xpresso tag
get it's gvnodemaster (see xpresso tag)
create the nodes required using the createnode call
make sure their data is set how i wanted
connect the nodes using the gvnode::addconnection routine

and that should in theory be it.

other than that calling other commands can be done either via messages or using the callcommand function (same as coffee there).
__________________
The Third Party | Homepage | My Reel
"You need to know what you're doing before you start, and to start because you need what you're doing."
 
Old 03 March 2006   #3
Oh, you haven't tried it. Hmmm...

Yes:

* Allocated an XPressoTag.
* Got the GvNodeMaster and Root GvNode.
* Created the Source/RangeMapper/Dest GvNodes
* Set the Object Links and Absolute/Relative respectively.

Now the part where 'not tried it' gets you (and me) in trouble. How do you add ports for dynamic description sliders that exist on these objects?

I tried this (entire section as outlined above):

        // Create an XPresso tag on the dial tag's ippBase
         xtag =                static_cast<XPressoTag*>(ippBase->GetTag(Texpresso));
         if (!(xtag && (xtag->GetName() == "IPP Slaved")))
         {
             xtag =            XPressoTag::Alloc();
             if (!xtag)        return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.xtag");
             ippBase->InsertTag(xtag);
             xtag->SetName("IPP Slaved");
         }
         // Create, Link, and Configure nodes
         nodeMaster =        xtag->GetNodeMaster();
         if (!nodeMaster)    return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.nodeMaster");
         root =                nodeMaster->GetRoot();
         if (!root)            return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.root");
         snode =                nodeMaster->CreateNode(root, ID_OPERATOR_OBJECT, NULL, x, y);
         if (!snode)            return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.snode");
         rnode =                nodeMaster->CreateNode(root, ID_OPERATOR_RANGEMAPPER, NULL, x+144, y);
         if (!rnode)            return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.rnode");
         dnode =                nodeMaster->CreateNode(root, ID_OPERATOR_OBJECT, NULL, x+288, y);
         if (!dnode)            return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.dnode");
         nbc =                snode->GetOpContainerInstance();
         nbc->SetLink(GV_OBJECT_OBJECT_ID,    driver);
         nbc->SetLong(GV_OBJECT_PATH_TYPE,    GV_OBJECT_PATH_TYPE_ABSOLUTE);
         nbc =                dnode->GetOpContainerInstance();
         nbc->SetLink(GV_OBJECT_OBJECT_ID,    ippBase);
         nbc->SetLong(GV_OBJECT_PATH_TYPE,    GV_OBJECT_PATH_TYPE_RELATIVE);
         // - Add Ports
         if (snode->AddPortIsOK(GV_PORT_OUTPUT, sindex))        sport =        snode->AddPort(GV_PORT_OUTPUT, sindex);
         else                return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.sport");
         if (dnode->AddPortIsOK(GV_PORT_INPUT, dindex))        dport =        dnode->AddPort(GV_PORT_INPUT,  dindex);
         else                return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.dport");
         roport =            rnode->GetOutPort(0);
         if (!roport)        return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.roport");
         riport =            rnode->GetInPort(0);
         if (!riport)        return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.riport");
 


The part below the comment "// - Add Ports", it returns with the error on the first check. 'sindex' and 'dindex' are the ID's of the dynamic sliders. You thought the same, right? It's wrong.

Calling other commands requires a command to be called. For, do a search of the entire 'resource' folder, "SETDRIVER", there is only an IDS string setting. I could not find a corresponding command to go with it. If you find a way to call "Set Driver" and "Set Driven (Relative)" with the appropriate A.M. parameter, let me know!
__________________
Greebler | InterPoser

C4D-R16, VS2012, Xcode 4.6
 
Old 03 March 2006   #4
You may need to get the list of the ports on your node first then iterate through the list and simply change the visibility of the ports rather than trying to allocate a port that probably already exists.
__________________
The Third Party | Homepage | My Reel
"You need to know what you're doing before you start, and to start because you need what you're doing."
 
Old 03 March 2006   #5
What I have just done (literally - that's why I'm here) is to find that GvNode::GetPortList() returns the 'available' ports on the node. You then must go through the list and call GvNode::GetPortDescription() so that you can correlate the portlist.id with the portdescription.name. This id is not the description id in the AM. It is something altogether different. Here's the code thus far (untidied):

// IPPDial.ResolveMasterDials
// - Convert Channel MasterDialEntries into Active MasterDials (XPresso tags)
//*---------------------------------------------------------------------------*
Bool IPPDial::ResolveMasterDials(BaseDocument* doc, AtomArray* atomArray, BaseObject* ippBase, BaseTag* tag, BOOL doUndo)
//*---------------------------------------------------------------------------*
{
	AutoAlloc<GvPortList>	portList;
	if (!portList)			return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.portList");
	XPressoTag*				xtag;
	GvNodeMaster*			nodeMaster;
	GvNode*					root;
	// Driver
	GvNode*					snode;
	GvPort*					sport;
	// RangeMapper
	GvNode*					rnode;
	GvPort*					riport;
	GvPort*					roport;
	// Driven
	GvNode*					dnode;
	GvPort*					dport;
	GvPortListEntry*		portLE;
	GvPortDescription		portDesc;
	BaseObject*				driver;
	BaseTag*				dial;
	BaseContainer*			bc =		tag->GetDataInstance();
	BaseContainer*			nbc;
	LONG					i;
	LONG					portCount;
	LONG					sindex;
	LONG					dindex =	bc->GetLong(IPPDIAL_INDEX);
	ULONG					flags;
	LONG					x = 8, y = 8;
	String					dportName =	bc->GetString(IPPDIAL_DIALNAME);
	String					nodeName =	ippBase->GetDataInstance()->GetString(IPP_INTERNALNAME)+"."+dportName;
	for (MasterDialEntry* md = firstMaster; md != lastMaster; md++)
	{
		// Find Driver slider
		driver =		md->bodypart.GetObject(atomArray);
		if (!(driver && driver->IsInstanceOf(ID_IPPBASE)))	continue;
		// - Get Dial and index
		for (dial = driver->GetFirstTag(); dial; dial = dial->GetNext())
		{
			if (!(dial->IsInstanceOf(ID_IPPDIALTAG) || dial->IsInstanceOf(ID_IPPMORPHTAG)))	continue;
			bc =	dial->GetDataInstance();
			if (bc->GetString(IPPDIAL_DIALNAME) == md->dialName) break;
		}
		if (!dial)	continue;
		sindex =			bc->GetLong(IPPDIAL_INDEX);

		// Create an XPresso tag on the dial tag's ippBase
		xtag =				static_cast<XPressoTag*>(ippBase->GetTag(Texpresso));
		if (!(xtag && (xtag->GetName() == "IPP Slaved")))
		{
			xtag =			XPressoTag::Alloc();
			if (!xtag)		return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.xtag");
			ippBase->InsertTag(xtag);
			xtag->SetName("IPP Slaved");
			if (doUndo)		doc->AddUndo(UNDO_NEW, xtag);
		}
		// Create, Link, and Configure nodes
		nodeMaster =		xtag->GetNodeMaster();
		if (!nodeMaster)	return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.nodeMaster");
		root =				nodeMaster->GetRoot();
		if (!root)			return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.root");
		snode =				nodeMaster->CreateNode(root, ID_OPERATOR_OBJECT, NULL, x, y);
		if (!snode)			return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.snode");
		rnode =				nodeMaster->CreateNode(root, ID_OPERATOR_RANGEMAPPER, NULL, x+144, y);
		if (!rnode)			return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.rnode");
		dnode =				nodeMaster->CreateNode(root, ID_OPERATOR_OBJECT, NULL, x+288, y);
		if (!dnode)			return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.dnode");
		nbc =				snode->GetOpContainerInstance();
		nbc->SetLink(GV_OBJECT_OBJECT_ID,	driver);
		nbc->SetLong(GV_OBJECT_PATH_TYPE,	GV_OBJECT_PATH_TYPE_ABSOLUTE);
		nbc =				dnode->GetOpContainerInstance();
		nbc->SetLink(GV_OBJECT_OBJECT_ID,	ippBase);
		nbc->SetLong(GV_OBJECT_PATH_TYPE,	GV_OBJECT_PATH_TYPE_RELATIVE);
		// - Add Ports
		roport =			rnode->GetOutPort(0);
		if (!roport)		return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.roport");
		riport =			rnode->GetInPort(0);
		if (!riport)		return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.riport");

		portList->FlushAll();
		snode->GetPortList(GV_PORT_OUTPUT, *portList);
		portCount =			portList->GetCount();
		for (i = 0; i < portCount; i++)
		{
			portLE =	portList->GetIndex(i);
			snode->GetPortDescription(GV_PORT_OUTPUT, portLE->id, &portDesc);
			if (portDesc.name == md->dialName) break;
		}
		if (i == portCount)	return ErrorException::Throw("Master Node Port not found!");
		if (!snode->AddPortIsOK(GV_PORT_OUTPUT, portLE->id))		return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.sport.NotOK");
		sport =				snode->AddPort(GV_PORT_OUTPUT, portLE->id);
		if (!sport)			return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.sport");
		portList->FlushAll();
		dnode->GetPortList(GV_PORT_INPUT, *portList);
		portCount =			portList->GetCount();
		for (i = 0; i < portCount; i++)
		{
			portLE =	portList->GetIndex(i);
			dnode->GetPortDescription(GV_PORT_INPUT, portLE->id, &portDesc);
			if (portDesc.name == dportName) break;
		}
		if (i == portCount)	return ErrorException::Throw("Slave Node Port not found!");
		if (!dnode->AddPortIsOK(GV_PORT_INPUT, portLE->id))			return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.dport.NotOK");
		dport =				dnode->AddPort(GV_PORT_INPUT, portLE->id);
		if (!dport)			return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.dport");
		/*
		// - Connect Driver to RangeMapper
		if (!nodeMaster->IsConnectionValid(snode, sport, rnode, riport, snode, sport, rnode, riport))	{ GePrint("InvalidConnection 1"); return FALSE; }
		if (!snode->AddConnection(snode, sport, rnode, riport))	return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.AddConnection");
		// - Connect RangeMapper to Driven
		if (!nodeMaster->IsConnectionValid(rnode, roport, dnode, dport, rnode, roport, dnode, dport))	{ GePrint("InvalidConnection 2"); return FALSE; }
		if (!rnode->AddConnection(rnode, roport, dnode, dport))	return ErrorException::Throw(GeLoadString(ERROR_MEMORY_TE  XT), "IPPDial.ResolveMasterDials.AddConnection");
		*/

		// Mark Driver as having Slaves
		flags =			(ULONG)bc->GetLong(IPPDIAL_TYPESUBFLAGS);
		bc->SetLong(IPPDIAL_TYPESUBFLAGS, (LONG)(flags & DIAL_FLAGS_SLAVES));
		y += 80;
	}
	EventAdd();
	GeFree(firstMaster);
	firstMaster = NULL;
	return TRUE;
}


At this rate, I'm going to be Lord of the Nodes (well, at least Loopy by the Nodes).

Robert
__________________
Greebler | InterPoser

C4D-R16, VS2012, Xcode 4.6
 
Old 03 March 2006   #6
Update: no luck with AddConnection().

I think that IsConnectionValid() sorta makes sense (why the distrust that one knows which ports are inputs and which are outputs is beyond me). So you send your source node and port and dest node and port. But you also send a set of pointers that takes them back, rearranged if the order is incorrect (?).

There are no errors with IsConnectionValid() or AddConnection(), but there are no connections (doh!). And if I manually connect the source output to the RangeMapper input and the RangeMapper output to the dest input, the RangeMapper turns yellow (I take it that this means something is askew with my source and dest ports).

Again, no idea. I've compiled every bit of GraphView code in the SDK docs - its a whopping 95 lines and is completely irrelevant to what I'm doing (except for the IsConnectionValid() code comprising 4 lines). A Google search on any number of related terms only points back to PluginCafe SDK online docs. And I did a thorough search of my entire Cinema 4D install for "SETDRIVER" and "Set Driven" case insensitive - nothing but that single IDS string identifier. I've looked at every GraphView entry in the documentation and every GraphView class/header/res in the Resource folder. Even Darf has thrown in the towel on using GraphView due to the overwhelming complexity of this enigmatic mess.

Going to look over every related thread at PluginCafe, but don't expect much. And I'll be bumping my thread up daily until there is a response or they come to take me away (oho, aha, ehe)! ;0)

Robert
__________________
Greebler | InterPoser

C4D-R16, VS2012, Xcode 4.6
 
Old 03 March 2006   #7
Thread automatically closed

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
__________________
CGTalk Policy/Legalities
Note that as CGTalk Members, you agree to the terms and conditions of using this website.
 
Thread Closed share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 01:16 PM.


Powered by vBulletin
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.