main
John Lauer README, license, states, models, verification log and provenance ef5470d 17h ago
#!/usr/bin/env python3
"""Finish a step2glb GLB of the 150060VS75000 datasheet-built model: name the materials, give the gold
terminals a metallic finish and the epoxy / substrate a dielectric one, and set the lens for its state.

  glb_post_materials.py IN.glb [OUT.glb] [--state auto|off|on|board]

  off   : water-clear lens, unlit, translucent (alpha 0.45) so the die shows through
  on    : lens in the 570 nm emitted colour, emissive (KHR_materials_emissive_strength), alpha 0.85
  board : lens opaque in its own base colour, no emission (board CAD / LOD use)
  auto  : off when the lens is water-clear, board when it is green (the default for thumbnails)

Materials are recognised by the base colours build_step.py writes (step2glb converts them to linear).
The on state differs from the off state ONLY in the lens material, as adom/component-states@1 requires.
"""
import sys
import pygltflib


def srgb2lin(c):
    return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4


SRC = {  # sRGB as written into the STEP by build_step.py, and by adom-chip-laser for the etch
    "substrate": (0.90, 0.90, 0.88),
    "terminal_gold": (0.85, 0.68, 0.35),
    "polarity_mark": (0.10, 0.65, 0.25),
    "die": (0.15, 0.15, 0.17),
    "lens_off": (0.86, 0.92, 0.93),
    "lens_on": (0.62, 0.95, 0.10),
}
LIN = {k: tuple(srgb2lin(c) for c in v) for k, v in SRC.items()}


def classify(rgb):
    if max(rgb) < 0.015:          # adom-chip-laser's near-black etch (sRGB 0.112 -> linear 0.012)
        return "laser_etch_mpn"
    best, d = None, 9
    for k, v in LIN.items():
        e = sum((a - b) ** 2 for a, b in zip(rgb, v))
        if e < d:
            best, d = k, e
    if d > 2e-3:
        return "laser_etch_mpn" if max(rgb) < 0.05 else None
    return best


def main():
    args = [a for a in sys.argv[1:] if not a.startswith("--")]
    state = "auto"
    if "--state" in sys.argv:
        state = sys.argv[sys.argv.index("--state") + 1]
        args = [a for a in args if a != state]
    src = args[0]
    out = args[1] if len(args) > 1 else src
    g = pygltflib.GLTF2().load(src)
    used = set(g.extensionsUsed or [])
    for m in g.materials:
        pbr = m.pbrMetallicRoughness
        k = classify(pbr.baseColorFactor[:3])
        if k is None:
            continue
        if k == "terminal_gold":
            m.name = "terminal_gold"; pbr.metallicFactor = 1.0; pbr.roughnessFactor = 0.35
        elif k in ("substrate", "die", "polarity_mark", "laser_etch_mpn"):
            m.name = k; pbr.metallicFactor = 0.0; pbr.roughnessFactor = 0.6
        elif k.startswith("lens"):
            st = state
            if st == "auto":
                st = "off" if k == "lens_off" else "board"
            pbr.metallicFactor = 0.0; pbr.roughnessFactor = 0.15
            if st == "off":
                m.name = "lens_water_clear"
                pbr.baseColorFactor = list(LIN["lens_off"]) + [0.45]
                m.alphaMode = "BLEND"; m.doubleSided = True; m.emissiveFactor = [0, 0, 0]
            elif st == "on":
                m.name = "lens_lit_570nm"
                pbr.baseColorFactor = list(LIN["lens_on"]) + [0.85]
                m.alphaMode = "BLEND"; m.doubleSided = True
                m.emissiveFactor = list(LIN["lens_on"])
                m.extensions = dict(m.extensions or {}, KHR_materials_emissive_strength={"emissiveStrength": 1.6})
                used.add("KHR_materials_emissive_strength")
            else:
                m.name = "lens_green" if k == "lens_on" else "lens_water_clear"
                pbr.baseColorFactor = list(pbr.baseColorFactor[:3]) + [1.0]
                m.alphaMode = "OPAQUE"; m.emissiveFactor = [0, 0, 0]
    g.extensionsUsed = sorted(used)
    g.save(out)
    print("OK: %s -> %s (%s)" % (src, out, ", ".join(m.name for m in g.materials)))


if __name__ == "__main__":
    main()