Загрузка данных


"""
Пластина-параллелограмм с надписью "REPUBLIC OF GAMERS" (без логотипа - место слева свободно).
Запуск: Blender -> Scripting -> Создать -> вставить код -> Текст -> Запустить скрипт.
Единицы: 1 единица Blender = 1 мм. Лицевая сторона смотрит в -Y (вид спереди: Numpad 1).
"""
import bpy
import bmesh
from math import pi

# ---------- Настройки ----------
W = 60.0            # ширина пластины по горизонтали, мм
H = 10.5            # высота, мм
SLOPE = 0.85        # наклон боковых граней (смещение по X на 1 мм высоты)
T = 1.2             # толщина пластины, мм
INSET = 0.9         # отступ внутренней панели от края, мм
RAISE = 0.15        # на сколько внутренняя панель выступает вперёд, мм
TEXT_X = -0.08      # начало текста: доля ширины от центра (0 = центр, -0.5 = левый край)
TEXT_SIZE = 3.3     # размер шрифта, мм
LINE_GAP = 3.7      # расстояние между строками, мм
LETTER_SPACE = 1.15 # межбуквенный интервал
LINE1, LINE2 = "REPUBLIC OF", "GAMERS"

PREFIX = "ROGPlate_"


# ---------- Материалы ----------
def make_material(name, c0, c1, metallic, rough, noise_scale=6.0, stretch=(1, 1, 1), bump=0.1):
    mat = bpy.data.materials.get(name) or bpy.data.materials.new(name)
    try:
        mat.use_nodes = True
    except Exception:
        pass
    nt = mat.node_tree
    nt.nodes.clear()
    out = nt.nodes.new("ShaderNodeOutputMaterial")
    bsdf = nt.nodes.new("ShaderNodeBsdfPrincipled")
    nt.links.new(bsdf.outputs["BSDF"], out.inputs["Surface"])
    bsdf.inputs["Base Color"].default_value = c1
    bsdf.inputs["Metallic"].default_value = metallic
    bsdf.inputs["Roughness"].default_value = rough
    try:
        tc = nt.nodes.new("ShaderNodeTexCoord")
        mp = nt.nodes.new("ShaderNodeMapping")
        nt.links.new(tc.outputs["Object"], mp.inputs["Vector"])
        mp.inputs["Scale"].default_value = tuple(noise_scale * s for s in stretch)
        nz = nt.nodes.new("ShaderNodeTexNoise")
        nz.inputs["Scale"].default_value = 1.0
        nz.inputs["Detail"].default_value = 5.0
        nt.links.new(mp.outputs["Vector"], nz.inputs["Vector"])
        ramp = nt.nodes.new("ShaderNodeValToRGB")
        ramp.color_ramp.elements[0].color = c0
        ramp.color_ramp.elements[1].color = c1
        nt.links.new(nz.outputs["Fac"], ramp.inputs["Fac"])
        nt.links.new(ramp.outputs["Color"], bsdf.inputs["Base Color"])
        bp = nt.nodes.new("ShaderNodeBump")
        bp.inputs["Strength"].default_value = bump
        bp.inputs["Distance"].default_value = 0.05
        nt.links.new(nz.outputs["Fac"], bp.inputs["Height"])
        nt.links.new(bp.outputs["Normal"], bsdf.inputs["Normal"])
    except Exception as ex:
        print("Текстуры материала", name, "не применены:", ex)
    return mat


# ---------- Геометрия ----------
def parallelogram(w, h, slope):
    """Параллелограмм (x, z) с центром в начале координат, нижняя сторона сдвинута вправо."""
    k = slope * h / 2
    return [(-w / 2 - k, h / 2), (w / 2 - k, h / 2), (w / 2 + k, -h / 2), (-w / 2 + k, -h / 2)]


def prism(name, pts, y0, y1, mat):
    """Призма вдоль оси Y по контуру pts в плоскости XZ."""
    n = len(pts)
    verts = [(x, y0, z) for x, z in pts] + [(x, y1, z) for x, z in pts]
    faces = [tuple(reversed(range(n))), tuple(n + i for i in range(n))]
    for i in range(n):
        j = (i + 1) % n
        faces.append((i, j, n + j, n + i))
    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)
    bm = bmesh.new()
    bm.from_mesh(mesh)
    bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
    bm.to_mesh(mesh)
    bm.free()
    obj.data.materials.append(mat)
    return obj


def add_text(body, x, y, z, mat, name):
    cu = bpy.data.curves.new(PREFIX + name, "FONT")
    cu.body = body
    cu.size = TEXT_SIZE
    cu.align_x = "LEFT"
    cu.align_y = "CENTER"
    cu.space_character = LETTER_SPACE
    cu.extrude = 0.04
    ob = bpy.data.objects.new(PREFIX + name, cu)
    bpy.context.collection.objects.link(ob)
    ob.rotation_euler = (pi / 2, 0, 0)      # лицом к -Y, строка вдоль +X
    ob.location = (x, y, z)
    ob.data.materials.append(mat)
    return ob


def main():
    for ob in list(bpy.data.objects):
        if ob.name.startswith(PREFIX):
            bpy.data.objects.remove(ob, do_unlink=True)

    m_plate = make_material(PREFIX + "Silver", (0.55, 0.57, 0.60, 1), (0.85, 0.86, 0.88, 1),
                            1.0, 0.35, noise_scale=7.0, stretch=(8.0, 0.3, 0.3), bump=0.2)
    m_panel = make_material(PREFIX + "Satin", (0.70, 0.71, 0.73, 1), (0.92, 0.93, 0.94, 1),
                            1.0, 0.5, noise_scale=12.0, stretch=(6.0, 0.3, 0.3), bump=0.08)
    m_text = make_material(PREFIX + "Black", (0.005, 0.005, 0.006, 1), (0.02, 0.02, 0.025, 1),
                           0.0, 0.25, noise_scale=40.0, bump=0.02)

    # основание и внутренняя панель
    plate = prism(PREFIX + "Plate", parallelogram(W, H, SLOPE), 0.0, T, m_plate)
    bev = plate.modifiers.new("Bevel", "BEVEL")
    bev.width = 0.25
    bev.segments = 3
    panel = prism(PREFIX + "Panel", parallelogram(W - 2 * INSET, H - 2 * INSET, SLOPE),
                  -RAISE, 0.05, m_panel)
    bev2 = panel.modifiers.new("Bevel", "BEVEL")
    bev2.width = 0.08
    bev2.segments = 2

    # две строки текста справа, слева место под логотип
    x = TEXT_X * W
    y = -RAISE - 0.03
    add_text(LINE1, x, y, +LINE_GAP / 2, m_text, "Line1")
    add_text(LINE2, x, y, -LINE_GAP / 2, m_text, "Line2")

    # свет
    ld = bpy.data.lights.new(PREFIX + "Light", "AREA")
    ld.energy = 3000
    ld.size = 60
    lo = bpy.data.objects.new(PREFIX + "Light", ld)
    bpy.context.collection.objects.link(lo)
    lo.location = (10, -60, 40)
    lo.rotation_euler = (pi / 2.6, 0, 0)

    try:
        for area in bpy.context.screen.areas:
            if area.type == "VIEW_3D":
                area.spaces[0].shading.type = "MATERIAL"
    except Exception as ex:
        print("Не удалось переключить вид:", ex)
    print("Готово: пластина REPUBLIC OF GAMERS")


main()