Загрузка данных
"""
HDMI (Type A) и DisplayPort - процедурные 3D-модели штекеров для Blender.
Запуск: Blender -> Scripting -> New -> вставить код -> Run Script (Alt+P).
Единицы: 1 единица Blender = 1 мм. Размеры приблизительные, по стандартным спецификациям.
"""
import bpy
import bmesh
from math import pi
# ---------- Настройки ----------
WALL = 0.3 # толщина металлического корпуса, мм
CABLE_R = 2.5 # радиус кабеля, мм
CABLE_LEN = 40.0 # длина кабеля, мм
# ---------- Материалы ----------
def make_material(name, color, metallic=0.0, roughness=0.5):
mat = bpy.data.materials.get(name) or bpy.data.materials.new(name)
mat.use_nodes = True
bsdf = mat.node_tree.nodes.get("Principled BSDF")
bsdf.inputs["Base Color"].default_value = color
bsdf.inputs["Metallic"].default_value = metallic
bsdf.inputs["Roughness"].default_value = roughness
return mat
MAT_METAL = make_material("Shell_Metal", (0.75, 0.77, 0.80, 1), 1.0, 0.25)
MAT_PLASTIC = make_material("Plastic_Black", (0.02, 0.02, 0.02, 1), 0.0, 0.45)
MAT_GOLD = make_material("Contact_Gold", (1.0, 0.77, 0.25, 1), 1.0, 0.2)
MAT_CABLE = make_material("Cable_Rubber", (0.01, 0.01, 0.01, 1), 0.0, 0.7)
def assign(obj, mat):
obj.data.materials.clear()
obj.data.materials.append(mat)
# ---------- Профили (сечение штекера, плоскость X-Z) ----------
def hdmi_profile(w, h, z0=0.0, c=1.0):
"""Трапеция HDMI Type A: сверху ровно, снизу скошенные углы."""
return [
(-w / 2, z0 + h),
(w / 2, z0 + h),
(w / 2, z0 + h * 0.5),
(w / 2 - c, z0),
(-w / 2 + c, z0),
(-w / 2, z0 + h * 0.5),
]
def dp_profile(w, h, z0=0.0, c=1.5):
"""DisplayPort: прямоугольник со срезанным верхним правым углом."""
return [
(-w / 2, z0),
(w / 2, z0),
(w / 2, z0 + h - c),
(w / 2 - c, z0 + h),
(-w / 2, z0 + h),
]
# ---------- Геометрические помощники ----------
def new_object(name, verts, faces, loc=(0, 0, 0)):
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(verts, [], faces)
mesh.update()
obj = bpy.data.objects.new(name, mesh)
bpy.context.collection.objects.link(obj)
obj.location = loc
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
bm.to_mesh(mesh)
bm.free()
return obj
def hollow_tube(name, outer, inner, depth, loc):
"""Полая труба вдоль оси Y по двум профилям с одинаковым числом точек."""
n = len(outer)
v = []
for (x, z) in outer:
v.append((x, 0, z)) # 0..n-1
for (x, z) in outer:
v.append((x, depth, z)) # n..2n-1
for (x, z) in inner:
v.append((x, 0, z)) # 2n..3n-1
for (x, z) in inner:
v.append((x, depth, z)) # 3n..4n-1
f = []
for i in range(n):
j = (i + 1) % n
f.append((i, j, n + j, n + i)) # наружная стенка
f.append((2 * n + i, 3 * n + i, 3 * n + j, 2 * n + j)) # внутренняя
f.append((i, 2 * n + i, 2 * n + j, j)) # передний торец
f.append((n + i, n + j, 3 * n + j, 3 * n + i)) # задний торец
return new_object(name, v, f, loc)
def box(name, size, center, parent=None):
bpy.ops.mesh.primitive_cube_add(size=1, location=center)
o = bpy.context.active_object
o.name = name
o.dimensions = size
bpy.ops.object.transform_apply(scale=True)
if parent:
o.parent = parent
return o
def rounded_box(name, size, center, bevel=1.2, parent=None):
o = box(name, size, center, parent)
mod = o.modifiers.new("Bevel", "BEVEL")
mod.width = bevel
mod.segments = 4
bpy.ops.object.shade_smooth()
return o
# ---------- Сборка штекера ----------
def build_plug(kind, origin_x):
if kind == "HDMI":
w, h, depth = 13.9, 4.45, 10.0
prof = hdmi_profile
pins_top, pins_bot = 10, 9
pitch = 1.0
else: # DisplayPort
w, h, depth = 16.1, 4.76, 10.0
prof = dp_profile
pins_top, pins_bot = 10, 10
pitch = 1.0
root = bpy.data.objects.new(f"{kind}_Plug", None)
bpy.context.collection.objects.link(root)
root.location = (origin_x, 0, 0)
# Металлический корпус
outer = prof(w, h, 0.0)
inner = prof(w - 2 * WALL, h - 2 * WALL, WALL, c=(1.0 if kind == "HDMI" else 1.5) - WALL)
shell = hollow_tube(f"{kind}_Shell", outer, inner, depth, (0, 0, 0))
shell.parent = root
assign(shell, MAT_METAL)
# Пластиковый язычок внутри
tongue_t = 1.0
tongue_w = w - 2 * WALL - 1.0
tongue = box(f"{kind}_Tongue", (tongue_w, depth - 1.0, tongue_t),
(0, depth / 2 + 0.5 - 0.5 + 0.0, h / 2), root)
tongue.location.y = (depth - 1.0) / 2 + 1.0
assign(tongue, MAT_PLASTIC)
# Контакты (два ряда)
cw, cl, ct = 0.5, depth - 2.5, 0.12
z_top = h / 2 + tongue_t / 2 + ct / 2
z_bot = h / 2 - tongue_t / 2 - ct / 2
for row, count, z in (("T", pins_top, z_top), ("B", pins_bot, z_bot)):
start = -(count - 1) * pitch / 2
for i in range(count):
c = box(f"{kind}_Pin_{row}{i + 1}", (cw, cl, ct),
(start + i * pitch, 1.5 + cl / 2, z), root)
assign(c, MAT_GOLD)
# Пластиковая «лодочка» (корпус разъёма)
boot = rounded_box(f"{kind}_Boot", (w + 4.0, 14.0, h + 4.0),
(0, depth + 7.0, h / 2), bevel=1.2, parent=root)
assign(boot, MAT_PLASTIC)
# Кабель
bpy.ops.mesh.primitive_cylinder_add(
radius=CABLE_R, depth=CABLE_LEN,
location=(0, depth + 14.0 + CABLE_LEN / 2, h / 2),
rotation=(pi / 2, 0, 0), vertices=32)
cable = bpy.context.active_object
cable.name = f"{kind}_Cable"
cable.parent = root
bpy.ops.object.shade_smooth()
assign(cable, MAT_CABLE)
return root
# ---------- Запуск ----------
def main():
# очистка старых объектов с такими именами
for ob in list(bpy.data.objects):
if ob.name.startswith(("HDMI_", "DisplayPort_", "DP_")):
bpy.data.objects.remove(ob, do_unlink=True)
build_plug("HDMI", -15.0)
build_plug("DisplayPort", 15.0)
# Мягкий свет и камера для быстрого рендера
if "Key_Light" not in bpy.data.objects:
bpy.ops.object.light_add(type="AREA", location=(0, -60, 50))
light = bpy.context.active_object
light.name = "Key_Light"
light.data.energy = 8000
light.data.size = 40
light.rotation_euler = (pi / 3.2, 0, 0)
print("Готово: HDMI слева, DisplayPort справа.")
main()