ブリッジ状の形状のオブジェクトをスクリプトで作成

初めに


このスクリプトの目的は、3DグラフィックスソフトウェアであるBlenderを使って、厚みのある半円と長方形の断面を持つ形状を自動生成することです。この形状は、特定の半径と厚みを持つ半円が長方形の上部に接続された形状で、奥行きも指定できます。このスクリプトは、建築や工業デザインなどにおいて特殊なプロファイルをモデリングする際に役立つでしょう。


パラメータの設定

このスクリプトではまず、形状のパラメータを設定します。

R = 1  # 半円の半径
H = 2  # 長方形の高さ
width = 0.5  # 幅の厚み
depth = 0.8  # 奥行き

ここでは半円の半径、長方形の高さ、半円断面の幅の厚み、および奥行きを定義しています。このパラメータを変更することで、形状の寸法を自在に調整できます。

メッシュとオブジェクトの生成

次に、Blender内で新しいメッシュとオブジェクトを作成し、シーンに追加します。

mesh = bpy.data.meshes.new("Thick_SemiCircle_Rectangle_Mesh")
obj = bpy.data.objects.new("Thick_SemiCircle_Rectangle", mesh)
bpy.context.collection.objects.link(obj)
bpy.context.view_layer.objects.active = obj
obj.select_set(True)

新しいメッシュデータを作成し、それをオブジェクトにリンクさせた後、Blenderのシーンに追加しています。また、この新しいオブジェクトをアクティブオブジェクトとして選択状態に設定します。

bmeshの使用開始

Blenderのbmeshモジュールを使用して、頂点やエッジ、面といった低レベルのメッシュ操作を行います。この例では、bm = bmesh.new()で新しいbmeshインスタンスを生成し、以降のメッシュ操作を管理しています。

幅を持たせた半円の断面を作成

半円形の断面を生成するために、指定したセグメント数でループを行います。

segments = 16  # 半円の分割数
outer_verts = []
inner_verts = []

for i in range(segments + 1):
    angle = math.pi * i / segments
    x_outer = R * math.cos(angle)
    z_outer = H + R * math.sin(angle)
    x_inner = (R - width) * math.cos(angle)
    z_inner = H + (R - width) * math.sin(angle)
    
    outer_verts.append(bm.verts.new((x_outer, 0, z_outer)))
    inner_verts.append(bm.verts.new((x_inner, 0, z_inner)))

づいて、半円の頂点を生成しています。math.cosおよびmath.sin関数を使用して、半円のXおよびZ座標を計算し、幅を持たせるために内側と外側の頂点リストをそれぞれ作成します。

長方形部分の断面を作成

次に、長方形の断面を追加します。ここでも内外の頂点リストを作成し、後の処理で連結して使用します。

rect_outer_verts = [
    (-R, 0, 0),
    (R, 0, 0)
]
rect_inner_verts = [
    (-(R - width), 0, width),
    (R - width, 0, width)
]


面の生成

半円部分と長方形部分の断面をつなぎ合わせて、面を作成していきます。面の生成は、外側の頂点と内側の頂点を順に結びながら行います。この処理は、頂点リストの順序が重要であるため、正確に順番を守りつつ面を作成します。

# フェースを作成(外側と内側の辺をつなぐ)
faces = []
for i in range(len(outer_verts) - 1):
    face = bm.faces.new((outer_verts[i], outer_verts[i + 1], inner_verts[i + 1], inner_verts[i]))
    # depthが正の場合のみ面の反転
    if depth > 0:
        face.normal_flip()
    faces.append(face)

このコードブロックでは、まず外側の頂点と内側の頂点を4点ずつまとめ、bm.faces.new関数で面(フェース)を作成しています。さらに、奥行き(depth)の値が正の場合には、face.normal_flip()を使用して面の法線方向を反転しています。Blenderでは、法線の方向が逆向きの場合、レンダリングにおいて面が見えなくなるため、この調整を行っています。

最後のセグメントの閉じる処理

半円の端の部分は最初と最後のセグメントを閉じる必要があります。ここで、最後のセグメントをつなげる面を作成し、形状を閉じます。

# 最後のセグメントを閉じる
closing_face = bm.faces.new((outer_verts[-1], outer_verts[0], inner_verts[0], inner_verts[-1]))
if depth > 0:
    closing_face.normal_flip()
faces.append(closing_face)

このコードでは、最終的に外側と内側のリストの最初と最後の頂点を使用して、半円の端を閉じる処理を行っています。これで、断面が完全に閉じられ、厚みのある形状を作成する準備が整います。


奥行き方向の処理(エクストルード)

次に、形状に奥行きを持たせるためにエクストルード処理を行います。エクストルード(押し出し)操作を使用して、作成した断面形状を指定した奥行き分だけ押し出し、立体的な形状に変換します。

# 奥行き方向の処理
r = bmesh.ops.extrude_face_region(bm, geom=faces)
verts = [e for e in r['geom'] if isinstance(e, bmesh.types.BMVert)]
bmesh.ops.translate(bm, vec=Vector((0, depth, 0)), verts=verts)

このコードブロックでは、bmesh.ops.extrude_face_region関数を使用して、作成した断面の面をエクストルードし、立体的な形状を生成しています。エクストルードによって生成された頂点をリストに格納し、bmesh.ops.translateを用いて奥行き方向(Y方向)に移動させ、指定したdepth分だけ押し出しています。

メッシュの更新と解放

最後に、作成したbmeshのデータをBlenderのメッシュオブジェクトに適用し、bmeshのリソースを解放して処理を終了します。

# メッシュの更新と解放
bm.to_mesh(mesh)
bm.free()

ここでは、bm.to_mesh(mesh)によって、bmeshで編集したデータをBlenderのメッシュデータとして確定させます。そして、bm.free()でbmeshのリソースを解放し、メモリを節約しています

ここから先は

1,862字

¥ 100