BlenderのPythonスクリプトで最初に覚えるべき内容をまとめたページは、このリンクにあります。
編集状態で、編集を継続 | オブジェクト状態で編集を終結 |
---|---|
import bmesh obj=bpy.data.objects['立方体'] bpy.ops.object.mode_set(mode='EDIT')# 下記の編集オブジェクト取得のため編集モードにする bm = bmesh.from_edit_mesh(obj.data) # 編集モード中のメッシュデータをBMeshに変換 ここで、bmを使った編集を行います。 bmesh.update_edit_mesh(obj.data) # BMeshを更新(編集モードで、bmは引き続き編集可能) |
import bmesh obj=bpy.data.objects['立方体'] bm = bmesh.new()# BMeshを作成 bm.from_mesh(obj.data) # オブジェクトのメッシュデータをBMeshに変換します。 ここで、bmを使った編集を行います。 (編集モードであれば、bmesh.update_edit_mesh(me) 随時更新可能) bm.to_mesh(me) # 最終的なステップの反映 bm.free()# BMeshを破棄 |
for face:BMFace in bm.faces: # 全ての面の走査 for edge:BMEdge in face.edges: # 全ての辺の走査 for vert:BMVert in edge.verts: # 全ての辺の走査 vert の頂点処理
import bmesh bpy.ops.mesh.primitive_cube_add(size=2, location=(-3, 0, -3), scale=(1, 1, 1)) # 立方体の生成 obj = bpy.context.active_object # 直前でアクセスしたオブジェクト bpy.ops.object.mode_set(mode='EDIT')# 下記の編集オブジェクト取得のため編集モードにする bpy.ops.mesh.bisect( plane_co=(0.0, 0.0, -3), # カット平面の基準点 plane_no=(0.0, 0.0, 1.0), # カット平面の法線ベクトル ) bm = bmesh.from_edit_mesh(obj.data) # BMeshのコピーを取得 for e in bm.edges: e.select = False # 頂点を全て非選択 bm.edges.ensure_lookup_table() edge=bm.edges[19] # 頂点(BMEdge)取得 edge.select = True # 必要ないが、視覚化して処理対象を明確にしています。 # 辺を指定した0.5の割合で分割し、新しい頂点を作成し、作成された辺と頂点のタプルを返します。 new_vert = bmesh.utils.edge_split(edge, edge.verts[0], 0.5) bmesh.update_edit_mesh(obj.data) # BMeshを更新 bm.free()
import sys sys.path.append('D:\\work') import beditsub obj=bpy.data.objects['立方体'] # ここで、GUI操作により、処理対象の辺だけを選択する。 beditsub.get_selected_edges_indices(obj) # 上記で選択した辺の添え字が、[19]と分かる # ここで、、GUI操作により、Alt+A で非選択にする。 beditsub.select_edges_by_indices(obj, [19]) # [19]の添え字の辺を選択状態にして、対象の辺かを確認
import bpy import bmesh from mathutils import Vector import bmesh bpy.ops.mesh.primitive_cube_add(size=2, location=(-3, 0, -3), scale=(1, 1, 1)) # 立方体の生成 bpy.ops.object.mode_set(mode='EDIT')# 下記の編集オブジェクト取得のため編集モードにする obj = bpy.context.active_object # 直前でアクセスしたオブジェクト bm = bmesh.from_edit_mesh(obj.data)# BMeshの生成とメッシュデータの取り込み bm.edges.ensure_lookup_table() edge = bm.edges[0]# 操作対象となる辺を決めて、変数に参照 (最初の辺を例に使用) v1, v2 = edge.verts # 辺の両端の頂点 direction = v2.co - v1.co # 辺の方向ベクトルを計算 new_position = v1.co + direction * 0.3 # 新しい頂点の位置 (例: X方向に 0.3 の位置) new_vert = bm.verts.new(new_position) # 頂点生成 # bm.edges.new((v1, new_vert)) # v1とnew_vertで辺生成 bmesh.update_edit_mesh(obj.data) # BMeshを更新
import bpy import bmesh def is_point_on_edge(point, edge): # edgeの位置が、edgeの辺上に存在するかを調べる関数 v1, v2 = edge.verts# エッジの始点と終点 edge_dir = (v2.co - v1.co).normalized() to_point = (point.co - v1.co) projected = edge_dir.dot(to_point) * edge_dir return (to_point - projected).length < 1e-6 mesh = bpy.data.meshes.new("TestMesh")# メッシュオブジェクトを作成 obj = bpy.data.objects.new("TestMesh", mesh) bpy.context.collection.objects.link(obj) bm = bmesh.new()# BMesh のセットアップ v1 = bm.verts.new((0, 0, 0)) # 開始頂点作成 v2 = bm.verts.new((2, 0, 0)) # 終了頂点作成 edge = bm.edges.new((v1, v2)) # エッジを作成 print("分割前のエッジ数:", len(bm.edges))# 分割前のエッジ数を確認 split_vert = bm.verts.new((1, 0, 0)) # エッジの中間に新しい頂点を作成 result = bmesh.ops.split_edges(bm, edges=[edge], verts=[split_vert])# エッジを分割 print("分割で使う頂点が辺の上に存在するか?:", is_point_on_edge(split_vert, edge)) print("split_edges の結果:", result)# 分割結果を確認 print("分割後のエッジ数:", len(bm.edges)) bm.to_mesh(mesh)# メッシュに反映 bm.free()
分割前のエッジ数: 1 分割で使う頂点が辺の上に存在するか?: True split_edges の結果: {'edges': [<BMEdge(0x000001E2F83D0550), index=0, verts=(0x000001E2A4B76510/0, 0x000001E2A4B76548/1)>]} 分割後のエッジ数: 1正しく分割出来た場合は2になるはず!!(1のままで分割できていない。)
import bpy import bmesh mesh = bpy.data.meshes.new("TestMesh")# メッシュオブジェクトを作成 obj = bpy.data.objects.new("TestMesh", mesh) bpy.context.collection.objects.link(obj) bm = bmesh.new()# BMesh のセットアップ v1 = bm.verts.new((0, 0, 0)) # 開始頂点作成 v2 = bm.verts.new((2, 0, 0)) # 終了頂点作成 edge = bm.edges.new((v1, v2)) # エッジを作成 print("分割前のエッジ数:", len(bm.edges))# 分割前のエッジ数を確認 result = bmesh.ops.bisect_edges(bm, edges=[edge], cuts=1)# エッジ上に新しい頂点を自動生成して分割 print("戻り値:", result) print("split_edges の結果:", result)# 分割結果を確認 print("分割後のエッジ数:", len(bm.edges)) def get_new_verts_from_bisect(result): # bisect_edges の結果から新しい頂点を取得" return [v for v in result['geom_split'] if isinstance(v, bmesh.types.BMVert)] split_vert=get_new_verts_from_bisect(result) print("生成された頂点:", split_vert[0] ) bm.to_mesh(mesh)# メッシュに反映 bm.free()
分割前のエッジ数: 1 戻り値: {'geom_split': [<BMVert(0x000001E2A4B62F80), index=2>, <BMEdge(0x000001E2F83D0F50), index=0, verts=(0x000001E2A4B62F80/2, 0x000001E2A4B62F48/1)>, <BMEdge(0x000001E2F83D0FA0), index=1, verts=(0x000001E2A4B62F10/0, 0x000001E2A4B62F80/2)>]} 分割後のエッジ数: 2 生成された頂点: <BMVert dead at 0x000001E2A356FF60>上記で分かるように、自動的に分割され、分割で生成された頂点(BMVert)を取得するために、別途でget_new_verts_from_bisectの関数を作っており、 分割で生成された頂点を、位置変更などの操作を行う場合、余計な手間が必要で効率が悪いと想像できます。
import sys sys.path.append('D:\\work') from importlib import reload import bpy import bmesh from mathutils import Vector from mathutils.geometry import intersect_point_line def create_vertex_on_nearest_edge(obj:bpy.types.Object, target_point:Vector)->Vector: rtnV:Vector=None # 戻り値 bm = bmesh.new() # bmeshのセットアップ bm.from_mesh(obj.data) closest_edge = None # 最も近い辺とその上の最近接位置を見つける closest_point_on_edge = None # 最も近い辺上の位置(Vector) min_distance = float('inf') for edge in bm.edges: v1, v2 = edge.verts[0].co, edge.verts[1].co # 辺の2頂点 point_on_edge, factor = intersect_point_line(target_point, v1, v2)# 最近接位置のVectorを取得 if factor < 0.0:# 点が辺の範囲内にあるか確認 point_on_edge = v1 elif factor > 1.0: point_on_edge = v2 distance = (target_point - point_on_edge).length #print(f' distance :{distance }, {edge} closest_point_on_edge:{closest_point_on_edge}') if distance < min_distance: min_distance = distance # 最短距離の更新 closest_edge = edge # 最も近い辺の更新 closest_point_on_edge = point_on_edge # 最も近い辺上の位置(Vector) # # if closest_edge and closest_point_on_edge:# 最近接辺上に頂点を生成 new_vert = bmesh.utils.edge_split(closest_edge, closest_edge.verts[0], 0.5) # 辺を分割 new_vert[1].co=closest_point_on_edge # 分割で生成した頂点の移動 rtnV=new_vert[1].co print(f'create point:{rtnV}') # bpy.ops.object.mode_set(mode='OBJECT') bm.to_mesh(obj.data)# bmeshをメッシュに反映 bm.free() return rtnV; 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_grid_add(x_subdivisions=1, y_subdivisions=2, size=5, location=(0, 0, 0), rotation=(3.14/2,0,0)) obj=bpy.context.active_object # 直前で生成したオブジェクト target_point = Vector((1.0, 0.1, 0.1)) point=create_vertex_on_nearest_edge(obj, target_point) print(f'生成位置:{point}') bpy.ops.object.mode_set(mode='EDIT') # エディットモードへ bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT') # 頂点モード、他の指定:'FACE' 'EDGE' bpy.ops.mesh.select_all(action='SELECT') # 全選択へ
生成位置:<Vector (1.0000, 0.0000, 0.0000)>
import bmesh obj=bpy.data.objects['立方体'] bpy.ops.object.mode_set(mode='EDIT')# 下記の編集オブジェクト取得のため編集モードにする bm = bmesh.from_edit_mesh(obj.data) # BMeshのコピーを取得 for v in bm.verts: v.select = False # 頂点を全て非選択 bm.verts.ensure_lookup_table() # 頂点のインデックスと実際の頂点データと対応関係を保証状態へ v1=bm.verts[1] # 頂点(BMVert)取得 (辺生成用の頂点の一つ) v4=bm.verts[4] # 頂点(BMVert)取得 (辺生成用でもう一方の頂点) bm.edges.new((v1, v4)) # v1とv4の頂点を結んだ辺の生成 v1.select = True # 必要ないが、視覚化して処理対象を明確にしています。 v4.select = True bmesh.update_edit_mesh(obj.data) # BMeshを更新 bm.free()
import bmesh obj=bpy.data.objects['立方体'] bpy.ops.object.mode_set(mode='EDIT')# 下記の編集オブジェクト取得のため編集モードにする bm = bmesh.from_edit_mesh(obj.data) # BMeshのコピーを取得 for v in bm.verts: v.select = False # 頂点を全て非選択 bm.faces.ensure_lookup_table() # 頂点のインデックスと実際の頂点データと対応関係を保証状態へ bm.verts.ensure_lookup_table() # 頂点のインデックスと実際の頂点データと対応関係を保証状態へ face=bm.faces[3] # 下記頂点を持つ面を指定する必要があります。 v1=bm.verts[1] # 頂点(BMVert)取得 (辺生成用の頂点の一つ) v4=bm.verts[4] # 頂点(BMVert)取得 (辺生成用でもう一方の頂点) bmesh.utils.face_split(face, v1, v4) # 面を、2つの頂点で結ぶ線で分割 v1.select = True # 必要ないが、視覚化して処理対象を明確にしています。 v4.select = True bmesh.update_edit_mesh(obj.data) # BMeshを更新 bm.free()
import bpy import bmesh # 面を分割するコードです。面を選択後、オブジェクトモードに変更して実行します。 obj=bpy.context.view_layer.objects.active # 一つの面を選択た状態のオブジェクトを取得 bpy.ops.object.mode_set(mode='EDIT')# 編集モードに変更 bpy.context.view_layer.update()# データ更新を明示的に実行 bm=bmesh.from_edit_mesh(obj.data)# BMeshにアクセス edges=[edge for edge in bm.edges if edge.select]# 選択した辺群を取得 print(f'edges:{edges} len(edges):{len(edges)}') try: result=bmesh.ops.subdivide_edges(bm, edges=edges, use_grid_fill=True, cuts=3)# 辺群で細分化 bmesh.update_edit_mesh(obj.data) # BMeshを更新 except Exception as e: print(f"エラーが発生しました: {e}")以下ではbmesh.ops.subdivide_edgesメソッドの利用例として、次のsplit_nearest_faceメソッドを定義して利用しています。
import bpy import bmesh from mathutils import Vector def split_nearest_face(obj, location, subdivisions=1): bpy.context.view_layer.objects.active = obj bpy.ops.object.mode_set(mode='EDIT')# 編集モードに変更 bm = bmesh.from_edit_mesh(obj.data)# BMeshにアクセス # target_location = Vector(location)# 指定位置の近傍の面 nearest_face = None # 指定位置の近傍の面 min_distance = float('inf') # floatの最大値 for face in bm.faces: # BMFaceの繰り返し closest_point = face.calc_center_median() # 面の中央となるVectorの取得 distance = (closest_point - target_location).length # 面との距離 if distance < min_distance : min_distance = distance # 最小距離を更新 nearest_face = face # BMFace # # #print(f'---------{nearest_face}') edges=[edge for edge in nearest_face.edges] # 面から辺 for edge in bm.edges: edge.select=True #print(f'選択辺の数:{len(edges)}, 選択辺:{edges}') result=bmesh.ops.subdivide_edges(bm, edges=edges, use_grid_fill=True, cuts=subdivisions)# 選択した辺を細分化 #new_vertices = result.get("geom_split", []) # 細分化によって新たに生成された要素 new_vertices = [elem for elem in result["geom_split"] if isinstance(elem, bmesh.types.BMVert)] # 新しく作られた頂点 #print(f'new_vertices:{new_vertices}') bpy.ops.mesh.select_all(action='DESELECT') # 全てを非選択へ for vert in new_vertices: vert.select=True bmesh.update_edit_mesh(obj.data) # BMeshを更新 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(size=1, location=(0, 0, 0), scale=(1, 1, 1)) # 立方体の生成 obj = bpy.context.object # アクティブなオブジェクトを取得 split_nearest_face(obj, location=(0, -2, 0), subdivisions=3)上記において、bmesh.ops.subdivide_edgesの戻り値のresultの内容は以下の辞書情報です。
{ "geom_split": [<BMVert>, <BMVert>, <BMEdge>, <BMEdge>, ...], # 細分化によって新たに生成された要素のみ。 "geom": [<BMVert>, <BMEdge>, <BMFace>, ...], # 細分化の影響を受けたすべての要素(既存のエッジやフェイスも含む) }
import bpy import bmesh from mathutils import Vector def split_nearest_faceN(obj, location, subdivisions=1, n=1): bpy.context.view_layer.objects.active = obj bpy.ops.object.mode_set(mode='EDIT')# 編集モードに変更 bm = bmesh.from_edit_mesh(obj.data)# BMeshにアクセス target_location = Vector(location)# 指定位置のVector化 facesN=[] # 走査対象の面 while n > 0: # 指定位置近傍の面をn個を、 nearest_face = None # 指定位置の近傍の面をfacesNに記憶 min_distance = float('inf') # floatの最大値 for face in bm.faces: # BMFaceの繰り返し if face in facesN: continue closest_point = face.calc_center_median() # 面の中央となるVectorの取得 distance = (closest_point - target_location).length # 面との距離 if distance < min_distance : min_distance = distance # 最小距離を更新 nearest_face = face # BMFace # # if nearest_face != None: facesN.append(nearest_face) n-=1 # print(f'分割対象の面の数:{len(facesN)}') bpy.ops.mesh.select_all(action='DESELECT') # EDITモードにおいて全体を非選択へ for nearest_face in facesN: nearest_face.select=True bpy.context.view_layer.update()# データ更新を明示的に実行 edges=[edge for edge in bm.edges if edge.select] # 細分化で使う面の辺群の取得 try: result=bmesh.ops.subdivide_edges(bm, edges=edges, use_grid_fill=True, cuts=subdivisions)# 辺群で細分化 bmesh.update_edit_mesh(obj.data) # BMeshを更新 except Exception as e: print(f"エラーが発生しました: {e}") # print(f'分割対象の辺の数:{len(edges)}') new_vertices = [elem for elem in result["geom_split"]] # 新しく作られた頂点や辺 for e in new_vertices: e.select=True bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT') bmesh.update_edit_mesh(obj.data) # BMeshを更新 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_grid_add(x_subdivisions=5, y_subdivisions=5, size=5, location=(0, 0, 0), rotation=(3.14/2,0,0)) obj=bpy.context.active_object # 直前で生成したオブジェクト bpy.ops.object.select_all(action='DESELECT') # OBJECTモードにおいて全体を非選択へ split_nearest_faceN(obj, location=[0, -0.1, 0], subdivisions=3, n=5)