I have learned that delegates in Gamma are a way to somehow insert nodes right in a link from the outside, and therefore naively perceived it somehow like the opposite of the concept of input and output pins. So I tried to create a process that allows to insert texture effects with a delegate which does not work… already talked about it in the chat, but I still don’t get why.
Attached is an example patch that works with a delegate for a number and a texture and I expected both to work the same way. What did I get wrong?
As discussed in the chat, the correct solution is to use interfaces. Delegates cannot hold state, they are only a function and will recreate the entire node + texture on every call.
if you really want to use delegates, you have to wrap the texturefx in a class and then create an instance of that class, hold it in a pad and only put the update operation into the delegate.
in any case, you have to wrap each texturefx in a class.
if you don’t want to do that, there is also a region called Anonymous it is a class definition in place, with create and update. you use it in a similar way as a delegate and call the create of it on the create of the surrounding context and the update in a delegate, like you tried in the first attempt.
Okay thank you. I have now some solutions how to implement this, but I still don’t understand why it is working with the float in the other example. Does this only apply for textures? Just trying to gain some understanding here about the case, because that might mean I have understood some principles a bit wrong in general.
@joreg, it says: “The node is short-lived. Its state will be re-created (and destroyed) every time this patch executes.”
The the + node in your float example is stateless, it doesn’t (have to) “remember” anything between two frames. You input a value, the result gets calculated and is returned all in one frame.
With nodes that (can) hold state the output may be dependend on a previous frame.
Okay I see… the warning in the delegate is the same like in an operation region. Everytime I had a warning about some stateful nodes in operations, I’ve put them into a process node, because in these it works.
The confusing thing for me is just that I’ve learned delegates as a way to insert nodes into a patch from the outside, so when dealing with a process node I would have expected it to work like that. But well, now I know that they don’t work with stateful nodes, thank you.
You wrap each TextureFX inside a class that implement a common interface. In my patch that interface is called ITextureFX and define one input and one output both of type Texture for one operation called Filter.
Every class that implement this interface MUST have the process Filter with one input and one output of type Texture. Think about an interface as a contract.
Now because both BlurFX and EdgeFX are of the same type (ITextureFX) and FXProcessor is using the operation Filter (common to all ITextureFX) you can swap BlurFX and EdgeFX easily.
Finally you have the FXProcessor node using the Filter operation.
Biggest advantage here is that FXProcessor doesn’t care about what specific you input, as long as it is of type ITextureFX it is the guarantee that it as the Filter operation, so you can create new TextureFX class, FXProcessor remains untouched.
FXProcessor could also be modified, the ITextureFX remains the same, so both part of the system aren’t tightly coupled and can grow separately. This would make sense later as the system gets bigger.
Gotcha! Thank you very much for this detailed information, learned a lot from it! Also really great to have learned through this example how to make a node with variable input pins, that can be changed using ctrl + plus/minus :)
One question on this one though - why did you decide to put the filter in the if region with the IsAssigned node? Just good practice or is there a specific reason?
It’s a NULL check because if there is no ITexture then the Filter will throw a not set to an instance of an object but it can be the case for the input texture too so you could have it for both.
It’s not specifically a good practice, sometimes you want to do something if the incoming data is NULL, let say replace the NULL texture by a default one. But sometimes you want to trigger an error because the incoming data should never be NULL and you must know that while developing.