Hi Maxime,
It's a script for Corona for Cinema 4D so I don't know if it'll be of any use to you. But from what I've understood materials and shaders structure are identical to the Standard C4D Material, so I've reduced the script to its essentials by removing few parameters, code below.
import c4d
import time
def iter_shaders(node):
"""Credits go to @ferdinand and @HerrMay over
at https://developers.maxon.net/ for the tree-traversal method
Yields all descendants of ``node`` in a truly iterative fashion.
The passed node itself is yielded as the first node and the node graph is
being traversed in depth first fashion.
This will not fail even on the most complex scenes due to truly
hierarchical iteration. The lookup table to do this, is here solved with
a dictionary which yields favorable look-up times in especially larger
scenes but results in a more convoluted code. The look-up could
also be solved with a list and then searching in the form ``if node in
lookupTable`` in it, resulting in cleaner code but worse runtime metrics
due to the difference in lookup times between list and dict collections.
"""
if not node:
return
# The lookup dictionary and a terminal node which is required due to the
# fact that this is truly iterative, and we otherwise would leak into the
# ancestors and siblings of the input node. The terminal node could be
# set to a different node, for example ``node.GetUp()`` to also include
# siblings of the passed in node.
visited = {}
terminator = node
while node:
if isinstance(node, c4d.BaseMaterial) and not node.GetFirstShader():
break
if isinstance(node, c4d.BaseMaterial) and node.GetFirstShader():
node = node.GetFirstShader()
# C4DAtom is not natively hashable, i.e., cannot be stored as a key
# in a dict, so we have to hash them by their unique id.
node_uuid = node.FindUniqueID(c4d.MAXON_CREATOR_ID)
if not node_uuid:
raise RuntimeError("Could not retrieve UUID for {}.".format(node))
# Yield the node when it has not been encountered before.
if not visited.get(bytes(node_uuid)):
yield node
visited[bytes(node_uuid)] = True
# Attempt to get the first child of the node and hash it.
child = node.GetDown()
if child:
child_uuid = child.FindUniqueID(c4d.MAXON_CREATOR_ID)
if not child_uuid:
raise RuntimeError(
"Could not retrieve UUID for {}.".format(child))
# Walk the graph in a depth first fashion.
if child and not visited.get(bytes(child_uuid)):
node = child
elif node == terminator:
break
elif node.GetNext():
node = node.GetNext()
else:
node = node.GetUp()
def convert_c4d_bitmap(doc, c4d_bitmap, remove=True):
# Create the bitmap object in memory
cr_bitmap = create_cr_bitmap_shader(
c4d_bitmap[c4d.BITMAPSHADER_FILENAME])
if isinstance(c4d_bitmap.GetUp(), c4d.BaseShader):
parent = c4d_bitmap.GetUp()
# Looking for the slot in the parent base
# container where the shader is inserted
parent_shader_slot = get_bc_id(parent.GetDataInstance(), c4d_bitmap)
cr_bitmap.InsertUnder(parent) # Insert the bitmap in the shader
doc.AddUndo(c4d.UNDOTYPE_NEW, cr_bitmap)
doc.AddUndo(c4d.UNDOTYPE_CHANGE, parent)
# Linking the bitmap in the right slot
parent[parent_shader_slot] = cr_bitmap
elif isinstance(c4d_bitmap.GetMain(), c4d.BaseMaterial):
parent = c4d_bitmap.GetMain()
parent_shader_slot = get_bc_id(parent.GetDataInstance(), c4d_bitmap)
parent.InsertShader(cr_bitmap)
doc.AddUndo(c4d.UNDOTYPE_NEW, cr_bitmap)
doc.AddUndo(c4d.UNDOTYPE_CHANGE, parent)
parent[parent_shader_slot] = cr_bitmap
# Removing the old bitmap
if remove:
doc.AddUndo(c4d.UNDOTYPE_DELETEOBJ, c4d_bitmap)
c4d_bitmap.Remove()
return cr_bitmap
def create_cr_bitmap_shader(filepath):
cr_bitmap = c4d.BaseShader(1036473)
cr_bitmap_bc = cr_bitmap.GetDataInstance()
cr_bitmap_bc.SetFilename(c4d.CORONA_BITMAP_FILENAME, filepath)
return cr_bitmap # Return the bitmap shader object for further material insertion
def get_bc_id(target_bc, target_value):
for index, value in target_bc:
if value == target_value:
return index
def main():
doc = c4d.documents.GetActiveDocument()
materials = doc.GetActiveMaterials()
doc.StartUndo()
# Performance measurements
tic = time.perf_counter()
c = 0
# Main loop
for material in materials:
mtl_shaders = []
# Step over non Corona Physical materials or Corona Legacy materials.
if material.GetType() not in (1056306, 1032100):
continue
print(f"{material = }")
for shader in iter_shaders(material):
if shader.GetRealType() == c4d.Xbitmap:
mtl_shaders.append(shader)
if mtl_shaders:
for i in mtl_shaders:
c += 1
convert_c4d_bitmap(doc, i)
print(end="\n")
# Performance measurements
toc = time.perf_counter()
print(
f"Bitmap conversion successful: {c} shaders converted in {toc - tic:0.4f} seconds ")
doc.EndUndo()
c4d.EventAdd()
if __name__ == '__main__':
main()
One thing worth mentionning : with this version, I've got the also a TypeError but the Traceback is different, now the AddUndo method raises an exception.
Traceback (most recent call last):
File "scriptmanager", line 162, in <module>
File "scriptmanager", line 147, in main
File "scriptmanager", line 99, in convert_c4d_bitmap
SystemError: <method 'AddUndo' of 'c4d.documents.BaseDocument' objects> returned a result with an exception set
Thank you