Web Top Page

面を選択しての処理 BlenderのPythonスクリプトの実験 01

Blender初心者が最初に覚えるべき内容をまとめたページは、このリンクにあります。
このページは、Blender(Portable_4.1.1)のPythonスクリプトでこのページで示す手法で実行できるソースです。
参考:https://docs.blender.org/api/current/
D:\work\test.pyでソースを作り、下記のコードを貼り付けて実行します。(使用している Python のバージョンは、'3.11.7' です。)

import sys
from importlib import reload
sys.path.append('D:\\work')
import test
#reload( test )

起動デフォルト'Cube'のトップの面を選択して移動するスクリプト例

import bpy

obj = bpy.data.objects['Cube'] # 指定オブジェクトを変数で管理
#bpy.ops.object.editmode_toggle() # オブジェクトモードとエディットモードの切り替え

bpy.ops.object.mode_set(mode='EDIT')# 下記の面選択にするため編集モードにする
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')# 面選択モード
bpy.ops.mesh.select_all(action='DESELECT') # 全て非選択へ (action='SELECT'で選択)

bpy.ops.object.mode_set(mode='OBJECT')# オブジェクトモードに切り替え
obj.data.polygons[0].select = True  # 面のインデックスを指定して選択(オブジェクトモードで可能)

bpy.ops.object.mode_set(mode='EDIT')# 上記添え字で選択したが編集モードにしないと、確認できないのでエディットモードの切り替え
bpy.ops.transform.translate(value=(1, 0, 1), orient_type='GLOBAL')# 選択面の平行移動(エディットモードで行わないとオブジェクト全体の移動になる)
上記のobj.data.polygons[添え字].selectで、添え字に対応する面は、次のダイダイ色の番号通りでした。(実験結果)

上記実行後のトップ面の添え字の番号0で、その面を移動しています。

次はbmeshで面を選択して移動する方法です。

前述は、オブジェクトモードでobj.data.polygonsを使った例ですが、ここではエディットモードでBMeshを操作する例です。 'Camera' と 'Light' 以外を全て削除してから、新しいcubeを生成追加して、名前をから'Cube2'に変更してから行っています。
生成追加後は、全体が選択状態なので、全てを非選択状態にしてから、トップ面を選択して移動させています。
import bpy
import os

for obj in bpy.data.objects:# 'Camera' と 'Light' 以外を全て削除する
    if obj.name=='Camera' or obj.name=='Light': continue
    bpy.data.objects.remove( obj )

bpy.ops.mesh.primitive_cube_add()#cubeを追加
obj= bpy.context.active_object # 上記生成直後のオブジェクトを取得
obj.name='立方体' # オブジェクトの名前変更

import bmesh
bpy.ops.object.mode_set(mode='EDIT')# 下記の編集オブジェクト取得のため編集モードにする
# 上記で、bpy.context.active_object.modeが'EDIT'になります。)
obj = bpy.context.edit_object # bpy.context.active_objectで取得してもよい。

bm = bmesh.from_edit_mesh(obj.data) # BMeshのコピーを取得(エディットモードで行わないとエラー)

for vert in bm.verts:vert.select_set(False)# すべての頂点の選択状態を解除します

for edge in bm.edges:edge.select_set(False)# すべての辺の選択状態を解除します

for face in bm.faces:face.select_set(False)# すべての面の選択状態を解除します
# bpy.ops.mesh.select_all(action='DESELECT') で全て非選択することも可能

bm.faces.ensure_lookup_table() # これを使わないと、下記でエラー
bm.faces[5].select = True# トップ面のインデックスを指定して選択

bpy.ops.transform.translate(value=(1, 0, 1), orient_type='GLOBAL')# 選択オブジェクトの平行移動(エディットモードで行わないと、辺の移動)

bmesh.update_edit_mesh(obj.data) # bmeshで、obj.dataを更新

bm.faces[5]がトップ面になっています。(前述のオブジェクトモードでのobj.data.polygons[0]がトップ面で、添え字の面が全く異なります)

ちょっとまとめると、
bpy.context.active_object.modeが'OBJECT'のオブジェクトモードでは、 obj.data.polygonsのリストで、各面を操作します。
(obj.data.polygons[0].select = True でトップ面を選択します。)
bpy.context.active_object.modeが'EDIT'のエディットモードでは、 bmesh.from_edit_meshでメッシュのコピーをbmesh取得し、そのfacesの面リストで操作し、それをオブジェクトに設定し直す方法を使います。
(BMeshをbmに取得すると、bm.faces[5].select = True でトップ面を選択します。)
それぞれのリストで使う添え字と面の対応が一致していない結果が得られました。

その後の調査で、起動時のデフォルト'Cube'と、後から追加した'立法体'において、面とその添え字の関係が違っていると分かりました。
今後、プログラムで操作する場合、起動時のデフォルト'Cube'を使わないようにします。
それであれば、下記のように追加した立法体のobj.data.polygons[5]がトップ面の操作で、bm.faces[5]と同じ指定で扱えます。




既存オブジェクトを削除して、円柱を追加し、奇数添え字面と0の添え字面を選択し、底面を非選択にするスクリプト例

'Camera' と 'Light' 以外を全て削除してから、新しいcylinder(円柱)を生成追加してから行っています。
生成追加後は、全体が選択状態ので、全てを非選択状態にしてから、奇数面を選択しています。
そして、0の添え字面を選択し、底面を非選択にしています。
import bpy
import os

for obj in bpy.data.objects:# 'Camera' と 'Light' 以外を全て削除する
    if obj.name=='Camera' or obj.name=='Light': continue
    bpy.data.objects.remove( obj )

bpy.ops.mesh.primitive_cylinder_add(vertices=32,radius=0.05,depth=0.05)#円柱を追加
obj= bpy.context.object # 上記生成直後のオブジェクトを取得「obj = bpy.context.active_object で取得することもできます。」
obj= bpy.data.objects['円柱'] # この例において、上記の行はこの行と同じオブジェクトがobjに設定される。
bpy.ops.object.mode_set(mode='OBJECT')# オブジェクトモードに切り替え
obj.select_set(False) # オブジェクトの選択を外しても、個々の面の選択は外れない結果が得られた。

bpy.ops.object.mode_set(mode='EDIT')# エディットモードの切り替え
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')# 面選択モード
bpy.ops.mesh.select_all(action='DESELECT') # 全て非選択へ (action='SELECT'で選択)
bpy.ops.object.mode_set(mode='OBJECT')# オブジェクトモードに切り替え
nn=len(obj.data.polygons)
print(f"number of faces:{nn}")#「number of faces:34」の表示

for i, ob in enumerate(obj.data.polygons):
    if i % 2 == 0: continue 
    print(f"[{i}] :{ob.select}") #奇数添え字面だけの選択
    obj.data.polygons[i].select = True 

obj.data.polygons[0].select = True# 側面の右回りで最も奥の面
# obj.data.polygons[30].select = True# トップ面
obj.data.polygons[33].select = False# 最後の面の選択(底面)
bpy.ops.object.mode_set(mode='EDIT')# エディットモードの切り替え
上記のobj.data.polygons[添え字].selectで、添え字に対応する面は、次のダイダイ色の番号通りでした。
実験結果によると、底の面は最後の添え字の33で、何故か上の面の添え字は30であった

次はbmeshで面を選択する方法です。(上記と同じ面を選択させています)

前述は、オブジェクトモードでobj.data.polygonsを使った例ですが、ここではエディットモードでBMeshを操作する例です。 'Camera' と 'Light' 以外を全て削除してから、新しいcylinder(円柱)を生成追加してから行っています。
生成追加後は、全体が選択状態ので、全てを非選択状態にしてから、奇数面を選択しています。
そして、0の添え字面を選択し、底面を非選択にしています。 実験結果によるとobj.data.polygonsと同じで、底の面は最後の添え字の33で、上の面の添え字は30であった
import bpy
import os

for obj in bpy.data.objects:# 'Camera' と 'Light' 以外を全て削除する
    if obj.name=='Camera' or obj.name=='Light': continue
    bpy.data.objects.remove( obj )

bpy.ops.mesh.primitive_cylinder_add(vertices=32,radius=0.05,depth=0.05)#円柱を追加
obj= bpy.context.active_object # 上記生成直後のオブジェクトを取得
obj.name='Cube2' # オブジェクトの名前変更

import bmesh
bpy.ops.object.mode_set(mode='EDIT')# 下記の編集オブジェクト取得のため編集モードにする
bpy.ops.mesh.select_all(action='DESELECT') # 全て非選択へ (action='SELECT'で選択)
obj = bpy.context.active_object

bm = bmesh.from_edit_mesh(obj.data) # BMeshのコピーを取得
nn=len(bm.faces)
print(f"number of faces:{nn}")#「number of faces:34」の表示

bm.faces.ensure_lookup_table() # これを使わないと、下記でエラー
for i, ob in enumerate(bm.faces):
    if i % 2 == 0: continue 
    print(f"[{i}] :{ob.select}") #奇数添え字面だけの選択
    bm.faces[i].select = True 

bm.faces[0].select = True# 側面の右回りで最も奥の面
#bm.faces[30].select = True# (トップ面)
bm.faces[33].select = False# 最後の面の選択(底面)

bmesh.update_edit_mesh(obj.data) # bmeshで、obj.dataを更新

mesh.polygons と BMesh でどちらを使う?

BlenderのPython APIでメッシュを操作する際に、 bpy.types.Meshとbmeshの両方が使われます。
bpy.types.Meshは基本的な操作に適しており、bmeshは高度な編集操作に適しています。