Convert C4D octane back to C4D Standard Shader script
script takes only the albdeo/diffuse channel
import c4d
from c4d import documents
def find_albedo_from_shaders(mat):
"""
Traversiert die Shaderliste eines Materials und versucht:
1) Bitmap (Xbitmap) -> Rückgabe ('texture', pfad)
2) Color shader (Xcolor) -> Rückgabe ('color', c4d.Vector)
3) Fallback: mat.GetAverageColor() -> ('color', Vector)
"""
if not mat:
return (None, None)
start = mat.GetFirstShader()
if not start:
# kein Shader: Fallback auf Preview/AverageColor
avg = mat.GetAverageColor()
return ('color', avg) if avg else (None, None)
# depth-first traversal (stack)
stack = [start]
seen = set()
while stack:
sh = stack.pop()
if not sh or sh in seen:
continue
seen.add(sh)
try:
# 1) Bitmap shader?
if sh.CheckType(c4d.Xbitmap):
try:
fn = sh[c4d.BITMAPSHADER_FILENAME]
if fn:
return ('texture', fn)
except Exception:
pass
# 2) Color shader?
if sh.CheckType(c4d.Xcolor):
try:
col = sh[c4d.COLORSHADER_COLOR]
if isinstance(col, c4d.Vector):
return ('color', col)
except Exception:
pass
except Exception:
# ignore errors but continue traversing children/siblings
pass
# push children and next sibling (if any)
try:
child = sh.GetDown()
if child:
stack.append(child)
except Exception:
pass
try:
nxt = sh.GetNext()
if nxt:
stack.append(nxt)
except Exception:
pass
# 3) fallback auf durchschnittliche Materialfarbe / Preview
avg = mat.GetAverageColor()
if avg:
return ('color', avg)
return (None, None)
def walk_objects(root):
"""Generator: durchläuft Objekt-Hierarchie (Depth-first)."""
obj = root
while obj:
yield obj
if obj.GetDown():
# zuerst Kind (rekursiv)
for o in walk_objects(obj.GetDown()):
yield o
obj = obj.GetNext()
def convert_octane_to_standard():
doc = documents.GetActiveDocument()
if not doc:
return
mat_map = {} # altes Octane-Material -> neues Standard-Material
# 1) Alle Materialien durchlaufen, Octane erkennen und konvertieren
for mat in doc.GetMaterials():
try:
if not mat.GetTypeName().lower().startswith("octane"):
continue
except Exception:
continue
print("Konvertiere:", mat.GetName())
result_type, result_value = find_albedo_from_shaders(mat)
# Neues Standard C4D Material erzeugen
new_mat = c4d.BaseMaterial(c4d.Mmaterial)
new_mat.SetName(mat.GetName() + "_STD")
# Color-Kanal aktivieren
new_mat[c4d.MATERIAL_USE_COLOR] = True
if result_type == 'texture' and result_value:
# Bitmap-Shader erstellen und in Color-Kanal stecken
bmp = c4d.BaseShader(c4d.Xbitmap)
try:
bmp[c4d.BITMAPSHADER_FILENAME] = result_value
except Exception:
pass
new_mat.InsertShader(bmp)
new_mat[c4d.MATERIAL_COLOR_SHADER] = bmp
print(" -> Texture übernommen:", result_value)
elif result_type == 'color' and result_value:
# Color Vector übernehmen (erwartet c4d.Vector)
try:
# Sicherstellen, dass Werte im 0..1 Bereich sind
col = result_value
if isinstance(col, c4d.Vector):
# clamp 0..1
col = c4d.Vector(max(0.0, min(1.0, col.x)),
max(0.0, min(1.0, col.y)),
max(0.0, min(1.0, col.z)))
else:
col = c4d.Vector(1.0)
new_mat[c4d.MATERIAL_COLOR_COLOR] = col
print(" -> Farbe übernommen:", col)
except Exception:
pass
else:
# nichts gefunden: leave default (weiss) oder avg fallback wurde schon versucht
print(" -> Keine Albedo gefunden; Standardfarbe bleibt.")
new_mat.Message(c4d.MSG_UPDATE)
doc.InsertMaterial(new_mat)
mat_map[mat] = new_mat
# 2) Remappen: alle Texture-Tags in Szene ersetzen
root = doc.GetFirstObject()
if root:
for obj in walk_objects(root):
try:
for tag in obj.GetTags():
if tag.CheckType(c4d.Ttexture):
old_mat = tag[c4d.TEXTURETAG_MATERIAL]
if old_mat in mat_map:
tag[c4d.TEXTURETAG_MATERIAL] = mat_map[old_mat]
except Exception:
pass
c4d.EventAdd()
print("✅ Umwandlung abgeschlossen. (Neue Materialien mit Suffix '_STD')")
if __name__ == '__main__':
convert_octane_to_standard()