Hello @indexofrefraction,
Thank you for reaching out to us. I am having a bit trouble understanding what you want to do, but from what I understood, you have a shader graph something like this:
MyMaterial M
+- BranchHead 'Shaders'
+- LayerShader L
and want to go to this:
MyMaterial M
+- BranchHead 'Shaders'
+- FilterShader F
+- LayerShader L
while also replacing all base links in the Material M that formerly pointed to the layer shader L with a link to the filter shader F. And your actual question is then (which you did not explicitly ask):
How do I find out which parameters of a node point to another node?
Answer
First of all, I would point out this thread as we recently dealt there with shader hierarchies, which can be anything else than trivial. I will not deal with complex hierarchies here, but when your layer shader is nested inside some other shader (and not directly attached to a material), you will have to.
To find out which parameter types a node has, and by extension if the node has a certain value as one of its parameter values, one must traverse the description of the node. The description of a node defines among other things the structure of its parameters. For a material we can look for all shader links (or a specific value) by testing for DESC_SHADERLINKFLAG
in the description data container. See the example at the end of this posting for details.
Cheers,
Ferdinand
The result for a material with a noise shader in its color channel:
Found a shader link parameter: (8000, 133, 5703).
Its value <c4d.BaseShader object called Noise/Noise with ID 1011116 at 1636161331392> is a shader of the material.
Found a shader link parameter: (8001, 133, 5703).
Found a shader link parameter: (8002, 133, 5703).
Found a shader link parameter: (8003, 133, 5703).
Found a shader link parameter: (8004, 133, 5703).
Found a shader link parameter: (526376, 133, 5703).
Found a shader link parameter: (526377, 133, 5703).
Found a shader link parameter: (526378, 133, 5703).
Found a shader link parameter: (526381, 133, 5703).
Found a shader link parameter: (526394, 133, 5703).
Found a shader link parameter: (526403, 133, 5703).
Found a shader link parameter: (526419, 133, 5703).
Found a shader link parameter: (8005, 133, 5703).
Found a shader link parameter: (8006, 133, 5703).
Found a shader link parameter: (8012, 133, 5703).
Found a shader link parameter: (8007, 133, 5703).
Found a shader link parameter: (8008, 133, 5703).
Found a shader link parameter: (8009, 133, 5703).
Found a shader link parameter: (3225, 133, 5703).
The code:
"""Traverses the description of a material to find shader link parameters with a specific value.
Run this in the script manager with at least one material in the scene.
"""
import c4d
doc: c4d.documents.BaseDocument
def main() -> None:
"""
"""
# Get the first material in the document.
material = doc.GetFirstMaterial()
# I am not going to deal with nested shaders here, and instead look at the simple case of all
# shaders being attached to the material directly and us only wanting to inspect such shaders.
# Get the shaders directly used by the material.
currentShader, shaders = material.GetFirstShader(), []
while currentShader:
shaders.append(currentShader)
currentShader = currentShader.GetNext()
# Now we are going to traverse the description of the material. Iterating over a description
# will yield a data container for the current parameter, its DescId, and a third parameter
# which is here irrelevant.
for bc, descId, _ in material.GetDescription(c4d.DESCFLAGS_DESC_NONE):
# We are looking for shader links which is a special case of base links. If a base link
# is a shader link, that information is stored in its description data container. The nice
# side effect is that this field is only written for base link parameters, so we do not
# have to check the parameter type anymore.
if bc[c4d.DESC_SHADERLINKFLAG] == True:
print (f"Found a shader link parameter: {descId}.")
# Get the value of the parameter.
value = material[descId]
if value in shaders:
print (f"\tIts value {value} is a shader of the material.")
if __name__ == '__main__':
main()