夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

BlenderPythonでマテリアルごとのポリゴンの選択を行う。

本日はBlender枠です。

BlenderPythonを使用してマテリアルごとにポリゴンの選択を行っていきます。

今回はBlender3.4で動作確認をおこなっています。

BlenderのバージョンによってはAPIが異なるためエラーが発生する可能性があります。

〇コード

今回は次のようなコードを記述しました。

import bpy

# アクティブなオブジェクトを取得
obj = bpy.context.active_object

# オブジェクトがメッシュであるか確認
if obj.type == 'MESH':
    # 編集モードに切り替え
    bpy.ops.object.mode_set(mode='EDIT')

    # ポリゴンの選択を解除
    bpy.ops.mesh.select_all(action='DESELECT')

    # マテリアルインデックスが0のマテリアルを取得
    material_index = 1
    material = obj.data.materials[material_index]

    # マテリアルを使用しているポリゴンを選択
    bpy.ops.object.mode_set(mode='OBJECT')  # オブジェクトモードに切り替え
    for poly in obj.data.polygons:
        if poly.material_index == material_index:
            poly.select = True
    bpy.ops.object.mode_set(mode='EDIT')  # 編集モードに切り替え
else:
    print("Object is not a mesh.")

ここでのコアはpoly.select = Trueで、選択しているメッシュを取得できます。

このコードを実行すると編集モードでマテリアルごとに選択が可能です。

〇マテリアルごとのポリゴンの数と頂点情報を取得する。

ここまではPyrhonを書かなくてもBlenderの標準機能で実現できます。

今回はPythonを用いて現在編集モードで選択中のメッシュの情報を取得します。

これによってマテリアルごとに頂点情報を得ることができます。

まずはBlenderで編集モードで選択している頂点のデータを取得します。

import bpy
import bmesh

# アクティブなオブジェクトを取得
obj = bpy.context.active_object

# オブジェクトがメッシュであるか確認
if obj.type == 'MESH':
    # 編集モードに切り替え
    bpy.ops.object.mode_set(mode='EDIT')

    # BMeshを作成
    bm = bmesh.from_edit_mesh(obj.data)

    # 選択された頂点の座標を取得
    selected_vertices = [v.co for v in bm.verts if v.select]

    # BMeshを解放
    bmesh.update_edit_mesh(obj.data)

    # 編集モードを終了
    # ここで編集モードを終了させる必要はありません

    # 取得した頂点情報を表示
    print("Selected Vertices:")
    for vertex in selected_vertices:
        print(f"Vertex: {vertex}")

else:
    print("Object is not a mesh.")

bmeshはBlenderでメッシュの情報を取得することができます。

上記二つを組み合わせたコードが次のようになります。

import bpy
import bmesh

# アクティブなオブジェクトを取得
obj = bpy.context.active_object

# オブジェクトがメッシュであるか確認
if obj.type == 'MESH':
    # 編集モードに切り替え
    bpy.ops.object.mode_set(mode='EDIT')

    # ポリゴンの選択を解除
    bpy.ops.mesh.select_all(action='DESELECT')

    # マテリアルインデックスが0のマテリアルを取得
    material_index = 0
    material = obj.data.materials[material_index]

    # マテリアルを使用しているポリゴンを選択
    bpy.ops.object.mode_set(mode='OBJECT')  # オブジェクトモードに切り替え
    for poly in obj.data.polygons:
        if poly.material_index == material_index:
            poly.select = True
    bpy.ops.object.mode_set(mode='EDIT')  # 編集モードに切り替え

    # BMeshを作成
    bm = bmesh.from_edit_mesh(obj.data)

    # 選択された頂点の座標を取得
    selected_vertices = [v.co for v in bm.verts if v.select]

    # BMeshを解放
    bmesh.update_edit_mesh(obj.data)

    # 取得した頂点情報を表示
    print("Selected Vertices:")
    for vertex in selected_vertices:
        print(f"Vertex: {vertex}")

else:
    print("Object is not a mesh.")

このコードではmaterial_index = 0に定義されたマテリアル番号によってメッシュが選択されそのメッシュの頂点情報が出力されます。

〇完成コード

最後にfor文でマテリアルごとにそれぞれ頂点の情報を取得していきます。

import bpy
import bmesh

# アクティブなオブジェクトを取得
obj = bpy.context.active_object

# オブジェクトがメッシュであるか確認
if obj.type == 'MESH':
    # オブジェクトに含まれるすべてのマテリアルに対して処理
    for material_index, material in enumerate(obj.data.materials):
        # 編集モードに切り替え
        bpy.ops.object.mode_set(mode='EDIT')

        # ポリゴンの選択を解除
        bpy.ops.mesh.select_all(action='DESELECT')

        # マテリアルを使用しているポリゴンを選択
        bpy.ops.object.mode_set(mode='OBJECT')  # オブジェクトモードに切り替え
        for poly in obj.data.polygons:
            if poly.material_index == material_index:
                poly.select = True
        bpy.ops.object.mode_set(mode='EDIT')  # 編集モードに切り替え

        # BMeshを作成
        bm = bmesh.from_edit_mesh(obj.data)

        # 選択された頂点の座標を取得
        selected_vertices = [v.co for v in bm.verts if v.select]

        # BMeshを解放
        bmesh.update_edit_mesh(obj.data)

        # 取得した頂点情報を表示
        print(f"Material {material_index} - Selected Vertices:")
        for vertex in selected_vertices:
            print(f"   Vertex: {vertex}")

else:
    print("Object is not a mesh.")

さらに法線、メッシュのインデックスを追加したものが次のようになります。

import bpy
import bmesh

# アクティブなオブジェクトを取得
obj = bpy.context.active_object

# オブジェクトがメッシュであるか確認
if obj.type == 'MESH':
    # オブジェクトに含まれるすべてのマテリアルに対して処理
    for material_index, material in enumerate(obj.data.materials):
        # 編集モードに切り替え
        bpy.ops.object.mode_set(mode='EDIT')

        # ポリゴンの選択を解除
        bpy.ops.mesh.select_all(action='DESELECT')

        # マテリアルを使用しているポリゴンを選択
        bpy.ops.object.mode_set(mode='OBJECT')  # オブジェクトモードに切り替え
        for poly in obj.data.polygons:
            if poly.material_index == material_index:
                poly.select = True
        bpy.ops.object.mode_set(mode='EDIT')  # 編集モードに切り替え

        # BMeshを作成
        bm = bmesh.from_edit_mesh(obj.data)

        # UVレイヤーを取得
        uv_layer = bm.loops.layers.uv.active if bm.loops.layers.uv else None

        # 選択された頂点の座標、法線、メッシュのインデックス(Triangles)、UVを取得
        selected_data = []
        selected_verts = set()  # 重複を防ぐために選択された頂点を格納するセット

        for face in bm.faces:
            for vert in face.verts:
                if vert.index not in selected_verts and vert.select:
                    selected_verts.add(vert.index)
                    if uv_layer is not None:
                        uv = vert.link_loops[0][uv_layer].uv
                        selected_data.append((vert.co, vert.normal, vert.index, uv))
                    else:
                        selected_data.append((vert.co, vert.normal, vert.index))

        # BMeshを解放
        bmesh.update_edit_mesh(obj.data)

        # 取得した情報を表示
        print(f"Material {material_index} - Selected Data:")
        for data in selected_data:
            print(f"   Vertex: {data[0]}, Normal: {data[1]}, Index: {data[2]}, UV: {data[3] if uv_layer is not None else None}")

else:
    print("Object is not a mesh.")

本日は以上です。