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()

Weitere Beiträge