main
John Lauer KiCad's library model checked against ams' limits and kept (foot 0.529 mm at the gauge plane, in 0.45..0.75); four LODs… cb10da4 14d ago
#!/usr/bin/env python3
"""Give a chip GLB physical materials, by the part each material is used on.
step2glb writes base colours only, so glTF's defaults (metallic 1, roughness 1) make every part a flat
rough metal. This sets, per part (node name from the STEP assembly):
  pins            -> "Adom.Leads (matte tin)": metallic 1.0, roughness 0.35, tin colour
  body            -> "Adom.Body (epoxy)": metallic 0.0, roughness 0.55, colour kept
  mark            -> "Adom.Pin1Mark": metallic 0.0, roughness 0.6, colour kept
  Adom.LaserEtch  -> metallic 0.0, roughness 0.7, colour kept (the reviewed mark)
Geometry, node transforms and the etch colour are untouched. Each material is used by one part only.
usage: materials.py in.glb [out.glb]   (in place when out is omitted)"""
import sys
from pygltflib import GLTF2, Material, PbrMetallicRoughness

TIN = [0.72, 0.72, 0.70, 1.0]          # linear; about sRGB 0.86 / 0.86 / 0.85, bright matte tin
SPEC = {
    "pins": ("Adom.Leads (matte tin)", 1.0, 0.35, TIN),
    "body": ("Adom.Body (epoxy)", 0.0, 0.55, None),
    "mark": ("Adom.Pin1Mark", 0.0, 0.6, None),
    "Adom.LaserEtch": (None, 0.0, 0.7, None),
}

# KiCad packages3D standard colours (linear, as step2glb writes them): black body, metal grey pins, light brown mark
KICAD = {"body": (0.019, 0.018, 0.018), "pins": (0.645, 0.638, 0.572), "mark": (0.435, 0.398, 0.316)}
def by_colour(m):
    c = (m.pbrMetallicRoughness.baseColorFactor or [0, 0, 0])[:3]
    for k, ref in KICAD.items():
        if all(abs(a - b) < 0.02 for a, b in zip(c, ref)):
            return k
    return None

def main(src, out):
    g = GLTF2().load(src)
    used = {}
    for n in g.nodes:
        if n.mesh is None:
            continue
        key0 = next((k for k in SPEC if (n.name or "").startswith(k)), None)
        for p in g.meshes[n.mesh].primitives:
            if p.material is None:
                continue
            # a KiCad library STEP is one part with three colours: tell body, pins and mark by KiCad's colours
            key = key0 or by_colour(g.materials[p.material])
            used.setdefault(p.material, set()).add(key)
            if key is None:
                continue
            # give this part its own material when another part shares it
            if len(used[p.material]) > 1:
                m = g.materials[p.material]
                g.materials.append(Material(name=m.name, pbrMetallicRoughness=PbrMetallicRoughness(
                    baseColorFactor=list(m.pbrMetallicRoughness.baseColorFactor)), doubleSided=m.doubleSided))
                p.material = len(g.materials) - 1
                used[p.material] = {key}
            name, metal, rough, col = SPEC[key]
            m = g.materials[p.material]
            pbr = m.pbrMetallicRoughness or PbrMetallicRoughness()
            pbr.metallicFactor, pbr.roughnessFactor = metal, rough
            if col:
                pbr.baseColorFactor = col
            m.pbrMetallicRoughness = pbr
            if name:
                m.name = name
    missing = [k for k in ("pins", "body") if not any(k in s for s in used.values())]
    if missing:
        sys.exit(f"ERROR: {src}: no {missing} part found (node names must come from the STEP assembly)")
    g.save(out)
    print(f"OK: {out}: " + ", ".join(f"{m.name} m{m.pbrMetallicRoughness.metallicFactor} r{m.pbrMetallicRoughness.roughnessFactor}" for m in g.materials))

if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else sys.argv[1])