Hello @indexofrefraction,
it is a bit tricky to answer your questions here. First of all, please follow our forum rules regarding creating new topics for new questions. The title of the posting was "Copy LayerShader to a new Material". Copying is different from creating such shader setup. One of your follow up questions was therefore out of scope for this topic. I have forked that question into How to Create and Populate a Layer Shader?.
Secondly, the topic you were referring to, with the code from @m_adam, contained incorrect code. See How to Create and Populate a Layer Shader? for more information on the subject, but in short, that code parented the shader contained in a layer shader to the wrong node.
does InsertShader do something different / more than the normal GeListNode Insert methods ?
Yes, it does. These are different branches. You seem to be unaware of the concept of branches. Shaders must be placed in the "Shaders" branch of a node, not in the normal hierarchical relations of the node (the layer shader is there an annoying exception, see the linked thread above).
Below you find a little script which lines out roughly the concept of branches, or in other words: How scene graphs are organized in the classic API of Cinema 4D.
Cheers,
Ferdinand
"""Briefly explains parts of the scene graph model of the Cinema 4D classic API.
Run this script with in the Script Manger, to have a more meaningful output, the scene should
contain at least one object, with at least one tag, and at least one material with at least one
shader.
"""
from typing import Optional
import c4d
doc: c4d.documents.BaseDocument # The active document
op: Optional[c4d.BaseObject] # The active object, None if unselected
TAB: str = " " # A two spaces tab
def PrintRelatedNodes(node: c4d.GeListNode, indent: int=0):
"""Yields all descendants in all node relations of #node.
Even in the classic API, scene elements are organized in a graph that contains more relations
than just hierarchical relations, just as in any other 3D DCC app. There multiple ways one
could look at this, e.g., as a poly-hierarchical taxonomy/tree, or as as an ontology/graph, but
the reason is simple, when we have for example the object #parent, with the child object #child,
and the tag #tag attached to #parent, we must be able to distinguish between the relation
#is_child_of and #is_tag_of.
E.g., we cannot express these relations as a hierarchy:
parent
+ - tag
+ - child
But instead must express it as a graph with dedicated relation types:
parent
/ \
#is_child #is_tag
/ \
child tag
Cinema 4D takes in the classic API more the view of a tree with a twist, which is realized with
the type GeListHead. Nodes can have "direct" hierarchical relations, and indirect ones over
branches. The example looks like this in the classic API of Cinema 4D:
parent -------tags_branch-------> GeListHead Branch Tags
+ - child + - tag
The relations of a node are tied together with GeListNode.GetBranchInfo() which returns the
branches of a node, i.e., all relations that are not direct hierarchial ones, where each
branch is represented by a GeListHead which then can contain hierarchial relations of arbitrary
complexity and also branches for each node in that hierarchy.
Almost everything is tied together in this manner in a classic API scene, documents, objects,
tags, tracks, curves (but not keys), materials, shaders, and other things that a user is usually
not aware of. This realizes in the classic API what is commonly referred to as a scene graph, a
traversable graph that contains all elements in a scene.
Note:
This function was written for educational purposes, it could be optimized and also does not
depict a classic API scene graph in its entirety, as it ignores the concept of caches.
"""
def RepresentNode(node: c4d.GeListNode) -> None:
"""Represents a node over its string representation and branches.
"""
# Print the node at the current indentation.
print(f"{TAB * indent}{node}")
# This node has no branches.
if node.GetBranchInfo() == None:
return
# Iterate over the branches of the node.
for item in node.GetBranchInfo():
# The GeListHead of this branch, it will contain all nodes as direct children which
# are in a direct relation of type #name with #node.
branchHead = item["head"]
# The name of the branch, i.e., the type od relation, as for example "objects", "tags",
# or "shaders".
name = item["name"]
# The type ID of the branch, for a tracks branch this will for example be c4d.CTrack. It
# describes the id of the base type of the nodes to find below the list head.
tid = item["id"]
# This branch is empty, i.e., the branch has been created, but no relations have been
# yet established.
if branchHead.GetDown() == None:
continue
# Print the name of the branch and unpack
print (f"{TAB * (indent + 1)}Branching into '{name}:'")
PrintRelatedNodes(branchHead, indent + 2)
if not isinstance(node, c4d.GeListNode):
return None
if indent == 0:
print (f"{'-' * 100}\n")
# Walk all direct hierarchial relations of #node.
while node:
RepresentNode(node)
if node.GetDown():
print (f"{TAB * (indent + 1)}Direct Children:'")
PrintRelatedNodes(node.GetDown(), indent + 2)
node = node.GetNext()
if indent == 0:
print (f"{'-' * 100}\n")
def main() -> None:
"""
"""
# We can use #PrintRelatedNodes to walk the relations of many node types ...
# A document ...
PrintRelatedNodes(doc)
# An object ...
obj = doc.GetFirstObject()
PrintRelatedNodes(obj)
# A tag ...
if obj:
tag = obj.GetFirstTag()
PrintRelatedNodes(tag)
# A material ...
material = doc.GetFirstMaterial()
PrintRelatedNodes(material)
# A shader ...
if material:
shader = material[c4d.MATERIAL_COLOR_SHADER]
PrintRelatedNodes(shader)
# and everything else that is a GeListNode (and therefore can have children and branches), as
# for example tracks, curves, layers, etc.
if __name__ == '__main__':
main()
And here you can find an example output for this scene:
