Insert a shader into a shader hierarchy
-
Hey,
Well, your code looks mostly okay at first glance, but you know that we state in our forum guidelines that we cannot debug code for users. What jumps to the eye however is that your
walker
function only takes hierarchies into account. At least for the layer shader this will work fine, but to be more robust, I would also traverse the shader branch of each node. The subject has been dealt with in the topic I linked to in my first posting.When your script is not working, please specify what is not working, and where your code fails.
Cheers,
Ferdinand -
hm, actually i think the script does work correctly.
i was additionally checking if the shader tree was correct
and i was doing this using my own shader walker
and it failed, it stopps walking after the Filter Shaderif i use your PrintRelatedNodes() everything looks ok
i will post a follow up question in the mentioned thread :
https://developers.maxon.net/forum/topic/14056/copy-layershader-to-a-new-material/5 -
ok, i think i figured out my issue mentioned above ..
it would be nice if this could be confirmed:materials and shaders can only have shader type nodes,
there must be a shader gelist head at the beginning and followed by normal nodes.
i guess this is was InsertShader() actually does.now if you InsertShader again in the middle of a tree, you get a second shader gelist head
and as expected this the point is where my simple shader traversing failed.
but.. if you insert the filter shader manually in the material manager,
there is no second shader gelist head, just the one at the beginning.so i guess that c4d accepts both ways,
but the proper way would be already to use InsertUnder if we're already in a shader type node list (?)
using InsertShader in the middle of a shader list is not necessary and bad practice (?)
a list with multiple shader gelist heads could (should?) be pruned of excessive heads (?) -
Hey @indexofrefraction,
but the proper way would be already to use InsertUnder if we're already in a shader type node list (?) using InsertShader in the middle of a shader list is not necessary and bad practice (?) a list with multiple shader gelist heads could (should?) be pruned of excessive heads (?)
Well, the answer to that all is sort of yesn't. I personally would for example consider the layer shader carrying its shader dependencies as direct children to be at least a regression of the classic API scene architecture, as the proper place would be the shader branch of the layer shader.
The layer shader engineers might have (somewhat) rightfully thought that it does not really matter, or even had some technical reason to store the shaders as direct children. There is nothing special about branches, they are just contextualized hierarchies. For an object and its tags, they are necessary, because if we would also store tags as direct children of objects, it would become very cumbersome to sort out what is what when tags and child objects would be mixed. For shaders this is not the case, as there is nothing which is naturally a child of shaders as child objects are of objects. But in the end, the 'right' place to store shaders attached to a
GeListNode
is its shader branch. And other shader developers, both internal external, might have solved this differently and let their shaders store their shader dependencies in the 'Shader' branch of their shader. Which at end forces other developers to understand what each shader type does and imitate it when it must be handled in code.There is no 'satisfying' answer, when a shader type stores its shader dependencies as direct children, you must use
InsertUnder
, and when it does store its shader dependencies in the shader branch, you must useInsertShader
(which is just a shortcut for callingInsertUnder
on the shader branchGeListHead
of that node).Cheers,
Ferdinand -
hi Ferdinand,
i noticed that there is actually an issue with excessive gelist heads....
if you have this :<c4d.BaseShader object called 'Filter/Filter' with ID 1011128 at 0x7fcebe74eeb0> + <c4d.GeListHead object at 0x7fcebe787a70> branch 'Shaders' + + <c4d.LayerShader object called 'Layer/Layer' with ID 1011123 at 0x7fcebe787ab0> + + + <c4d.BaseShader object called 'Noise/Noise' with ID 1011116 at 0x7fcebe787b90>
GetUp() on the LayerShader seems to return None,
which is a problem if - like in this case - you need to know if a shader is in the middle inside a shader tree,
or if it is at the top / a direct child of the materialthis makes something like this necessary to properly "insert" a shader ....
if isinstance(target, c4d.BaseShader): shader.InsertUnder(target) else: target.InsertShader(shader)
-
Hey @indexofrefraction,
well, this is not surprising, as the
LayerShader
is not in a hierarchical relation with theGeListHead
but a branch relation. You must call GetListHead on aGeListNode
to retrieve its head, i.e., the branch which is containing the node.Cheers,
Ferdinand -
I also stumbled on this just today, and had the suspicion that BaseList2D::InsertShader() isn't the right function for this. Thank you @ferdinand for verifying that.
While it totally makes sense to use GeListNode::InsertUnder() when inserting shaders under shaders inside the shaders branch, I think it should totally be mentioned in the SDK docs. Ideally, as a big fat
@note
for the Doxygen description of BaseList2D::InsertShader(), as well as in the Shader section of the BaseList2D Manual.Cheers,
Frank -
Hey @fwilleke80,
Thank you for reaching out to us. If I remember correctly, we briefly talked about this internally when this thread was created. The problem is, as lined out above,
GeListNode::InsertUnder()
is not the unambiguously correct function to insert a shader below a shader, and by conventionBaseList2D::InsertShader()
is neither. It depends on the implementation of the shader what is correct and what not.So, flat out stating something like 'shaders which have other shaders as dependencies will carry them as direct children' as a warning in
BaseList2D::InsertShader()
can simply not be guaranteed to be correct. When the question was recent, I checked a couple of other shader types as the fusion shader, the filter shader, etc., and they all behaved the same as the layer shader, i.e., they carried their dependencies as direct children. But this only came by convention and not by definition, and there could certainly be shaders out there which handle this differently.However, and long story short, I understand your concerns and will add a small note, although a bit more defensively stated. In the end you must check yourself what each shader type does.
Cheers,
Ferdinand -
Then maybe it should be made an official guideline to insert shaders into materials with InsertShader(), and into a hierarchy of shaders with InsertUnder()
You're right, I think we did talk about this before. But I totally forgot, since it's not in the docs.
-
And, as a tip for other developers, I want to add that the "Active Object Dialog" example plugin from the C++ SDK does a fabulous job exposing things like this, and visualizing the document's node hierarchy.
Whoever wrote that back in the days still deserves a medal
-
this "Active Object Dialog" plugin looks very interesting
if it would be understandably described how to compile it / the c++ sdk, we could check it out
but sadly this got so complicated that you need a degree in IT to do it. .-) -
@indexofrefraction said in Insert a shader into a shader hierarchy:
this "Active Object Dialog" plugin looks very interesting
if it would be understandably described how to compile it / the c++ sdk, we could check it out
but sadly this got so complicated that you need a degree in IT to do it. .-)The first video in my tutorial series shows you how to compile the SDK example plugins.
-
Thanks a lot kbar.. i'll watch and learn!
still... the hurdles to compile a c++ plugin are extremely high nowadays.
it would be great if the sdk plugins would be downloadable ready compiled as well