Multiple Output in Node Plugin

hstewarth

New member
I am curious how to created multiple Node output in node plugin. I am
desiring to have evaluation output more than one value. I know this
can be done because 3d texture nodes do it.

It would be really use full if had a simple but complete 3d texture
node that would output color, alpha and bump.

The one I am working on now is not related to 3d texture but also I
desired have multiple outputs.

Also it would be nice to know about to setup in Node panels, a fix set of values selected by preset collection of values. For types and such.
 

jameswillmott

NewTek Developer
Outputting more than one value is quite simple, just set up your structure with as many NodeOutpuID variables as you need, and then assign to them using

Code:
typedef struct st_node {
NodeOutputID diffuse,spec,reflect,refract;
} node;


And in the Create() function....

inst->diffuse = nodeOutputFunc->create(inst->nodeID, NOT_COLOR,"Diffuse Shading");
inst->specular = nodeOutputFunc->create(inst->nodeID, NOT_COLOR,"Specular Shading");
inst->refract = nodeOutputFunc->create(inst->nodeID, NOT_COLOR,"Refract Shading");
inst->reflect = nodeOutputFunc->create(inst->nodeID, NOT_COLOR,"Reflect Shading");
 

hstewarth

New member
jameswillmott said:
Outputting more than one value is quite simple, just set up your structure with as many NodeOutpuID variables as you need, and then assign to them using

That is actually the easy part, maybe my real question is in Evaluation function. How do you handle that? But thanks anyway.

Also I am curious if is there a there a way to find a node ID - unique Number for each node instanced on render. IE if I use this on a texture on an object, and include multiple copies of each object - I desired this value to be different. I notice that value is Void * for Node - I could covert it to long and use it.
 

jameswillmott

NewTek Developer
Sorry, I misunderstood.

It's pretty easy, your Evaluation function has this template...

Evaluate(iColor *inst, LWNodalAccess *na, NodeOutputID nid, NodeValue nv)

nid refers to the output being evaluated, so just compare nid with each of your outputs like this...

Code:
if(nid==inst->diffuse)//diffuse output is being evaluated.
if(nid==inst->specular)//specularoutput is being evaluated.
etc...
 

hstewarth

New member
jameswillmott said:
Sorry, I misunderstood.

It's pretty easy, your Evaluation function has this template...

Evaluate(iColor *inst, LWNodalAccess *na, NodeOutputID nid, NodeValue nv)

nid refers to the output being evaluated, so just compare nid with each of your outputs like this...

Code:
if(nid==inst->diffuse)//diffuse output is being evaluated.
if(nid==inst->specular)//specularoutput is being evaluated.
etc...

Oh...Thanks, that should do it.

So each output is evaluated seperately - do you know which order? just in case there is ever any dependencies.
 

jameswillmott

NewTek Developer
I don't know off hand, I think it depends on your nodegraph as much as anything as to which order your outputs are evaluated. Since you can't set up nodes that affect themselves ( even in a roundabout way ) I don't think it matters.

Good luck.
 

hstewarth

New member
I got somethnng in the works - but I had issues with NOT_INTEGER and no values in xpanels - so I switch them to scalars.

But I believe what is describe here appears to work.
 

Lightwolf

obfuscated SDK hacker
hstewarth said:
So each output is evaluated seperately - do you know which order? just in case there is ever any dependencies.
The order is not fixed. In general you should not rely on dependencies at all, especially since nodes can run in a multi-threaded context. Basically, evaluate everything you need per output without relying on any of the other outputs.

That is imho one of the major drawbacks of the current nodal system.

Cheers,
Mike
 

hstewarth

New member
Lightwolf said:
The order is not fixed. In general you should not rely on dependencies at all, especially since nodes can run in a multi-threaded context. Basically, evaluate everything you need per output without relying on any of the other outputs.

That is imho one of the major drawbacks of the current nodal system.

Cheers,
Mike

Dioes this also mean that if you have an context information then you can't assume it is valid since it is multi-threaded.

I don't have anything that needs - each evaluation of output is recalculated for common information for each output.

Stewart
 

hstewarth

New member
Well I have couple issue with my Plugin right now..

Right now I have 5 group areas which 1 group is related to common attribute for the Node and the other 4 groups coresponded to 4 outputs.

On the common group, 2 of 4 input ( last ones ) are enable and the first 2 are disable.

On the 4 groups responding to output, the first 2 groups are disable and last 2 grousp are enable. Each group has 2 similar inputs.

I think I also may have an issue with save state - because it crash with my node in editor on save. but loads find.

Also on the enable/disable - if I first go in all inputs are enable in Panel but some as I entered an input - the one that are disable gray out.

Stewart
 

Lightwolf

obfuscated SDK hacker
hstewarth said:
Dioes this also mean that if you have an context information then you can't assume it is valid since it is multi-threaded.
You can, but you should only read from it, not write to it (i.e. to cache data).

You have one set of context data per instance of your plugin, which in turn may be evaluated by multiple threads.

Cheers,
Mike
 

hstewarth

New member
Lightwolf said:
You can, but you should only read from it, not write to it (i.e. to cache data).

You have one set of context data per instance of your plugin, which in turn may be evaluated by multiple threads.

Cheers,
Mike

I am curious if you can write to instance information, how can use as Increment value in NewTime method for a Node. For example disiring to increment a value use in alorgorithm with a predefined or user defined value.
 

Sensei

TrueArt Support
You have LWNodalAccess->ResourceContextIndex passed to node evaluation function.. So in theory it can be used as index to array where are thread specific data stored..

f.e.
instead of
data->diffuse = diffuse;

try
data->diffuse[ access->ResourceContextIndex ] = diffuse

and later reading by
diffuse = data->diffuse[ access->ResourceContextIndex ];
 

hstewarth

New member
Sensei said:
You have LWNodalAccess->ResourceContextIndex passed to node evaluation function.. So in theory it can be used as index to array where are thread specific data stored..

f.e.
instead of
data->diffuse = diffuse;

try
data->diffuse[ access->ResourceContextIndex ] = diffuse

and later reading by
diffuse = data->diffuse[ access->ResourceContextIndex ];


Very interesting, do you have do anything special with definition of the variable.
 

Sensei

TrueArt Support
Of course you will have to allocate enough space for array.. If you do it manually with new operator it looks like:

setup:
diffuse = new double|float[ number_of_threads ];

cleanup:
if( diffuse != NULL ) delete [] diffuse;

Or static declaration:
double|float diffuse[ number_of_threads ];

(in both cases don't forget to clear array! Don't expect new to clear it!)

Currently max number of threads is constant 8, but in future it might be changed (especially when 4 and more core CPU will be available).. If you plan to make just one version of plug-in use dynamic way and let user choose how many threads they want..

Better always check in evaluation function whether ResourceContextIndex is in your allocated space range, node will not work correctly, but at least won't crash..
 

Sensei

TrueArt Support
BTW, if it crashes or doesn't work it probably means that CPU cache memory overwrites array area of other CPU (don't test it on only Hyper Threading CPU! It can bring false result, because cache in them can be shared between CPU instances).. Cache pipe-line is usually much longer than sizeof( float ) or sizeof( double ).. In that case replace new[] and delete[] by operating system specific memory allocating function with attribute write-through and/or not-cacheable..

The other way might be allocating array for pointer to another allocated area.. In Create() allocate new double *[] and in evaluation before using *diffuse[ resourcecontextindex ] check whether it's NULL and if so use new again and fill that space.. Memory allocating functions are usually aware of single CPU cache pipe-line size and round up to the nearest good value even if you try to allocate very few bytes..
 

hstewarth

New member
I should have dual 5160 in the next week of two - the Superworkstation 7045a-3 is on the site and near release. But this is a Core 2 Base Woodcrest - I believe Core 2 have shared cache on them.

Right now - I am very frustrated that some of my controls are not enable when view from Panel. I am dead stop with this plugin until I fix this problem.

I have will create another thread specifically on this issue.
 
Top Bottom