【blender/python】階段生成スクリプト試作1 メモ

以下のコードをblenderの「Scripting」タブに貼り付けて実行(▶︎)するとどうなるかのメモ。

import bpy

import mathutils


# 既存のオブジェクトを削除

bpy.ops.object.select_all(action='SELECT')

bpy.ops.object.delete(use_global=False)


# ========= パラメータ =========

step_width = 1.0        # 階段の幅(X方向)

step_height = 0.18      # 1段の高さ(Z方向)

step_depth = 0.28       # 1段の奥行(Y方向)

num_steps = 12          # 段数

board_thickness = 0.03  # 踏み板の厚み


rail_height = 0.9       # 手すり高さ

post_radius = 0.02      # 支柱半径

rail_radius = 0.03      # 手すり半径

post_spacing = 2        # 支柱を何段ごとに置くか

# =============================


# --- 階段(踏み板+蹴込み板) ---

for i in range(num_steps):

    y = i * step_depth

    z = i * step_height


    # 踏み板

    bpy.ops.mesh.primitive_cube_add(

        size=2,  # ★ 幅の扱いをシンプルに

        location=(0, y, z + board_thickness/2)

    )

    step = bpy.context.active_object

    step.scale = (step_width/2, step_depth/2, board_thickness/2)


    # 蹴込み板(立ち板)

    if i > 0:

        bpy.ops.mesh.primitive_cube_add(

            size=2,

            location=(0, y - step_depth/2 + 0.01, z - step_height/2 + board_thickness/2)

        )

        riser = bpy.context.active_object

        riser.scale = (step_width/2, 0.01, step_height/2)


# --- 手すり支柱 ---

posts = []

for i in range(0, num_steps+1, post_spacing):

    y = i * step_depth

    z = i * step_height

    for side in (-1, 1):

        x = side * (step_width/2)

        bpy.ops.mesh.primitive_cylinder_add(

            radius=post_radius,

            depth=rail_height,

            location=(x, y, z + rail_height/2)

        )

        posts.append(bpy.context.active_object)


# --- 手すり(左右) ---

def create_rail(side):

    x = side * (step_width/2)


    start = mathutils.Vector((x, 0, rail_height))

    end   = mathutils.Vector((x, num_steps*step_depth, num_steps*step_height + rail_height))

    vec   = end - start

    length = vec.length

    mid = (start + end) / 2


    bpy.ops.mesh.primitive_cylinder_add(

        radius=rail_radius,

        depth=length,

        location=mid

    )

    rail = bpy.context.active_object

    rail.rotation_mode = 'QUATERNION'

    rail.rotation_quaternion = vec.to_track_quat('Z', 'Y')


create_rail(1)

create_rail(-1)






import bpy

import mathutils


# 既存のオブジェクトを削除

bpy.ops.object.select_all(action='SELECT')

bpy.ops.object.delete(use_global=False)


# ========= パラメータ =========

step_width = 1.2        # 階段の幅

step_height = 0.18

step_depth = 0.28

num_steps = 10

board_thickness = 0.04


rail_height = 1.0       # 手すり高さ

# =============================


# --- 踏み板と蹴込み板 ---

for i in range(num_steps):

    y = i * step_depth

    z = i * step_height


    # 踏み板

    bpy.ops.mesh.primitive_cube_add(size=2, location=(0, y, z + board_thickness/2))

    step = bpy.context.active_object

    step.scale = (step_width/2, step_depth/2, board_thickness/2)


    # 蹴込み板

    if i > 0:

        bpy.ops.mesh.primitive_cube_add(size=2, location=(0, y - step_depth/2, z - step_height/2 + board_thickness/2))

        riser = bpy.context.active_object

        riser.scale = (step_width/2, 0.01, step_height/2)


# --- バラスター(装飾支柱) ---

for i in range(num_steps+1):

    y = i * step_depth

    z = i * step_height

    for side in (-1, 1):

        x = side * (step_width/2 - 0.05)


        # 胴体(踏み板から手すりまで)

        bpy.ops.mesh.primitive_cylinder_add(

            radius=0.025,

            depth=rail_height,

            location=(x, y, z + rail_height/2)

        )

        body = bpy.context.active_object


        # 下の膨らみ

        bpy.ops.mesh.primitive_uv_sphere_add(radius=0.05, location=(x, y, z + 0.05))

        bottom_sphere = bpy.context.active_object


        # 中間の膨らみ(くびれ感)

        bpy.ops.mesh.primitive_uv_sphere_add(radius=0.04, location=(x, y, z + rail_height/2))

        mid_sphere = bpy.context.active_object


        # 上の膨らみ ← ★追加

        bpy.ops.mesh.primitive_uv_sphere_add(radius=0.05, location=(x, y, z + rail_height - 0.05))

        top_sphere = bpy.context.active_object


# --- 親柱(太い支柱) ---

for side in (-1, 1):

    for y, z in [(0, 0), (num_steps*step_depth, num_steps*step_height)]:

        x = side * (step_width/2 - 0.05)

        bpy.ops.mesh.primitive_cube_add(size=2, location=(x, y, z + rail_height/2))

        post = bpy.context.active_object

        post.scale = (0.1, 0.1, rail_height/2)


# --- 手すり ---

def create_rail(side):

    x = side * (step_width/2 - 0.05)

    start = mathutils.Vector((x, 0, rail_height))

    end   = mathutils.Vector((x, num_steps*step_depth, num_steps*step_height + rail_height))

    vec   = end - start

    length = vec.length

    mid = (start + end) / 2


    bpy.ops.mesh.primitive_cylinder_add(radius=0.05, depth=length, location=mid)

    rail = bpy.context.active_object

    rail.rotation_mode = 'QUATERNION'

    rail.rotation_quaternion = vec.to_track_quat('Z', 'Y')


create_rail(1)

create_rail(-1)





import bpy

import mathutils


# 例: 外部ファイルのパス(必要に応じて変更)

filepath = "任意のデザインの柱のblenderファイルへのパス.blend"

directory = filepath + "/Object/"

object_name = "立方体"  # .blend 内の柱のオブジェクト名


step_width = 1.2

step_height = 0.18

step_depth = 0.28

num_steps = 10

board_thickness = 0.04


rail_height = 1.0

# =======================


# --- シーンをクリア ---

bpy.ops.object.select_all(action='SELECT')

bpy.ops.object.delete(use_global=False)


# --- 読み込み ---

bpy.ops.wm.append(

    filepath=directory + object_name,

    directory=directory,

    filename=object_name

)

template = bpy.data.objects[object_name]


# --- ワールド座標のバウンディングボックスで高さを取得 ---

bpy.context.view_layer.update()

world_bb = [template.matrix_world @ mathutils.Vector(corner) for corner in template.bound_box]

z_min = min(v.z for v in world_bb)

z_max = max(v.z for v in world_bb)

original_height = z_max - z_min


# --- 一様スケール(既存scaleを考慮) ---

if original_height > 0:

    scale_factor = rail_height / original_height

    template.scale = tuple(s * scale_factor for s in template.scale)

    bpy.context.view_layer.update()

    print(f"バラスター高さ {original_height:.3f} → {rail_height:.3f}")

else:

    scale_factor = 1.0


# --- 階段(踏み板+蹴込み板) ---

for i in range(num_steps):

    y = i * step_depth

    z = i * step_height


    bpy.ops.mesh.primitive_cube_add(size=2, location=(0, y, z + board_thickness/2))

    step = bpy.context.active_object

    step.scale = (step_width/2, step_depth/2, board_thickness/2)


    if i > 0:

        bpy.ops.mesh.primitive_cube_add(size=2, location=(0, y - step_depth/2, z - step_height/2 + board_thickness/2))

        riser = bpy.context.active_object

        riser.scale = (step_width/2, 0.01, step_height/2)


# --- バラスター配置 ---

for i in range(num_steps+1):

    y = i * step_depth

    z = i * step_height

    for side in (-1, 1):

        x = side * (step_width/2 - 0.05)


        baluster = template.copy()

        baluster.data = template.data.copy()

        bpy.context.collection.objects.link(baluster)


        bpy.context.view_layer.update()


        # コピー後の底面を取得

        bb_world = [baluster.matrix_world @ mathutils.Vector(corner) for corner in baluster.bound_box]

        z_min_copy = min(v.z for v in bb_world)


        # 踏み板上に底面が来るよう補正

        baluster.location = (x, y, z - z_min_copy)


# --- テンプレート削除 ---

bpy.data.objects.remove(template, do_unlink=True)


# --- 親柱 ---

for side in (-1, 1):

    for y, z in [(0, 0), (num_steps*step_depth, num_steps*step_height)]:

        x = side * (step_width/2 - 0.05)

        bpy.ops.mesh.primitive_cube_add(size=2, location=(x, y, z + rail_height/2))

        post = bpy.context.active_object

        post.scale = (0.1, 0.1, rail_height/2)


# --- 手すり ---

def create_rail(side):

    x = side * (step_width/2 - 0.05)

    start = mathutils.Vector((x, 0, rail_height))

    end   = mathutils.Vector((x, num_steps*step_depth, num_steps*step_height + rail_height))

    vec   = end - start

    length = vec.length

    mid = (start + end) / 2


    bpy.ops.mesh.primitive_cylinder_add(radius=0.05, depth=length, location=mid)

    rail = bpy.context.active_object

    rail.rotation_mode = 'QUATERNION'

    rail.rotation_quaternion = vec.to_track_quat('Z', 'Y')


create_rail(1)

create_rail(-1)







※上の画像では適当に真ん中をくびれさせた立方体を柱のデザインとして使用。

def create_flat_rail(side, rail_width=0.2, rail_thickness=0.1, bevel=0.2):
    x = side * (step_width/2 - 0.05)

    start = mathutils.Vector((x, 0, rail_height))
    end   = mathutils.Vector((x, num_steps*step_depth, num_steps*step_height + rail_height))
    vec   = end - start
    length = vec.length
    mid = (start + end) / 2

    # Cube追加
    bpy.ops.mesh.primitive_cube_add(size=2, location=mid)
    rail = bpy.context.active_object

    # 形を整える
    rail.scale = (rail_width/2, length/2, rail_thickness/2)
    rail.rotation_mode = 'QUATERNION'
    rail.rotation_quaternion = vec.to_track_quat('Y', 'Z')

    # ベベルモディファイア
    mod = rail.modifiers.new(name="Bevel", type='BEVEL')
    mod.width = bevel
    mod.segments = 2
    mod.profile = 0.7
    mod.limit_method = 'NONE'   # ← 制限なしで全エッジをベベル

    # スムースシェーディング
    bpy.ops.object.shade_smooth()


# 実行(左右2本)
create_flat_rail(1)
create_flat_rail(-1)

とすればエスカレーターみたいな平たい手すりになるがベベルのせいで端の処理が気になる。

コメント

このブログの人気の投稿

旧バージョンのiMovieを入れる方法

TWRP公式サポートされてないAndroid端末でシステムインストールした話

[UTAU] Garagebandで作ったボーカルメロディーをUTAUに読ませる