molecule
12 V to 5 V / 1 A Buck Molecule (TPS54202), built in Altium Designer
Public Unreviewedby Colby Knox
A 12 V to 5 V, 1 A TPS54202 buck molecule designed and built by AI in Altium Designer from one sentence, with the Altium project, Gerbers, BOM, simulation and current and heat analysis.
main
Colby Knox
Board GLB with the Gerber artwork (mask, copper, ENIG, silkscreen) baked into its top and bottom faces and real materia…
f6f12e0
4d ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
#!/usr/bin/env python3
"""Bake the board's Gerber artwork and real finishes into the Altium board GLB, so any glTF viewer (the wiki's
3D viewer) shows what the renders show.
bake_board_glb.py <board.glb> <top.png> <bottom.png> <out.glb>
- Materials: the STEP export leaves every material at glTF's default metallicFactor 1.0, so plastic, FR4 and
silkscreen render as polished metal. Metal only for gold, tin and nickel tones (the render's rule), else
metallic 0.05; roughness 0.35 for metal, 0.55 otherwise.
- Faces: top.png / bottom.png (textures.py: mask, copper, ENIG openings, silkscreen, holes from the Gerbers and
drill file) as two alpha-masked quads, 12 um above the board's top face (z 0) and below its bottom face
(z -0.4115 mm), over the 28 x 20 mm outline. bottom.png is already mirrored for viewing from below.
"""
import struct, sys
import numpy as np
from pygltflib import GLTF2, Image, Texture, Sampler, Material, PbrMetallicRoughness, TextureInfo, Mesh, Primitive, \
Attributes, Node, Accessor, BufferView
src, top_png, bot_png, dst = sys.argv[1:5]
W, H, ZT, ZB, EPS = 0.028, 0.020, 0.0, -0.00041148, 0.000012
g = GLTF2().load(src)
blob = bytearray(g.binary_blob())
# materials: real finishes instead of all-metal
for m in g.materials:
r, gg, b = m.pbrMetallicRoughness.baseColorFactor[:3]
metal = (r > 0.55 and gg > 0.35 and b < 0.3) or (r > 0.5 and abs(r - gg) < 0.06 and abs(gg - b) < 0.08 and r < 0.8)
m.pbrMetallicRoughness.metallicFactor = 0.85 if metal else 0.05
m.pbrMetallicRoughness.roughnessFactor = 0.35 if metal else 0.55
def add(data, target=None):
while len(blob) % 4:
blob.append(0)
off = len(blob); blob.extend(data)
g.bufferViews.append(BufferView(buffer=0, byteOffset=off, byteLength=len(data), target=target))
return len(g.bufferViews) - 1
def quad(z, normal_z, flip_u, img_bytes, name):
# corners (x, y): u runs along x (reversed on the bottom, which the texture already mirrors), glTF v = 0 at the image top
xy = [(0, 0), (W, 0), (W, H), (0, H)]
pos = np.array([[x, y, z] for x, y in xy], np.float32)
nrm = np.array([[0, 0, normal_z]] * 4, np.float32)
uv = np.array([[(1 - x / W) if flip_u else x / W, 1 - y / H] for x, y in xy], np.float32)
idx = np.array([0, 1, 2, 0, 2, 3] if normal_z > 0 else [0, 2, 1, 0, 3, 2], np.uint16)
acc = []
for arr, typ, ct, tgt in ((pos, "VEC3", 5126, 34962), (nrm, "VEC3", 5126, 34962), (uv, "VEC2", 5126, 34962), (idx, "SCALAR", 5123, 34963)):
bv = add(arr.tobytes(), tgt)
a = Accessor(bufferView=bv, componentType=ct, count=len(arr), type=typ)
if arr is pos:
a.min, a.max = pos.min(0).tolist(), pos.max(0).tolist()
g.accessors.append(a); acc.append(len(g.accessors) - 1)
g.images.append(Image(bufferView=add(img_bytes), mimeType="image/png", name=name))
g.samplers = g.samplers or []
if not g.samplers:
g.samplers.append(Sampler(magFilter=9729, minFilter=9987, wrapS=33071, wrapT=33071))
g.textures.append(Texture(source=len(g.images) - 1, sampler=0))
g.materials.append(Material(name=name, alphaMode="MASK", alphaCutoff=0.5, doubleSided=False,
pbrMetallicRoughness=PbrMetallicRoughness(baseColorTexture=TextureInfo(index=len(g.textures) - 1),
metallicFactor=0.0, roughnessFactor=0.32)))
g.meshes.append(Mesh(name=name, primitives=[Primitive(attributes=Attributes(POSITION=acc[0], NORMAL=acc[1], TEXCOORD_0=acc[2]),
indices=acc[3], material=len(g.materials) - 1)]))
g.nodes.append(Node(name=name, mesh=len(g.meshes) - 1))
g.nodes[0].children.append(len(g.nodes) - 1)
g.images = g.images or []; g.textures = g.textures or []
quad(ZT + EPS, 1, False, open(top_png, "rb").read(), "Board top (mask, copper, silkscreen from the Gerbers)")
quad(ZB - EPS, -1, True, open(bot_png, "rb").read(), "Board bottom (mask, copper, silkscreen from the Gerbers)")
g.buffers[0].byteLength = len(blob)
g.set_binary_blob(bytes(blob))
g.save_binary(dst)
print(dst, len(blob), "bytes buffer;", len(g.materials), "materials,", len(g.meshes), "meshes")