詳細は、script内のcommentの通り
以下のurlも参考になります
- BlenderをPythonで操作する - Qiita
- Blender PythonのMeshデータアクセスのチートシート - Qiita
- Blender bpy でvertexとfaceとedgeを追加 | ぬの部屋(仮)
import bpy import copy import math import bmesh import sys from mathutils import Vector def main(): #全object削除 remove_all_obj() #cube 追加 new_obj_1 = add_cube("MeshCube.000", ( 1,1,1)) new_obj_2 = add_cube("MeshCube.001", (-1,1,1)) # 存在する全meshの結合 union_all_objects() # クリーンアップ( 重複頂点の削除等 ) cleanup_mesh_object( new_obj_2.name ) # 各頂点の座標をprint() chk_mesh_verts( new_obj_2.name ) # オブジェクトをコピー copy_mesh_object( new_obj_2.name, (4,3,2)) # 移動,拡大/縮小,回転 transform_mesh_object(new_obj_2.name, (0,0,0), (0,0,0), (3,3,3)) # 移動,拡大/縮小,回転のクリア clear_transform( new_obj_2.name ) # 頂点を移動 move_vertex(new_obj_2.name, (1,2,3)) # boundingboxやlocal座標、global座標の表示 disp_coords(new_obj_2.name) # objectを分割 divide_mesh_object( new_obj_2.name ) def disp_coords(obj_name): # 一旦、全てを選択解除 for ob in bpy.context.scene.objects: ob.select_set(False) ob = bpy.data.objects.get( obj_name ) if not ob: return ob.select_set(True) bpy.context.view_layer.objects.active = ob boundbox = ob.bound_box for vert in boundbox: print( "BOUNDBOX VERT",vert[0],vert[1],vert[2] ) bpy.ops.object.mode_set(mode='EDIT', toggle=False) meshdata = bmesh.from_edit_mesh(ob.data) # meshデータ取得 meshdata.select_mode = {'VERT'} # 面選択modeへ # グローバル座標への変換行列 # https://docs.blender.org/api/current/bpy.types.Object.html#bpy.types.Object.matrix_world matrix_world = ob.matrix_world # ちなみに global→local とする逆行列は以下 mwi = ob.matrix_world.inverted() # globalの座標・回転・スケールを取得する場合は以下 (translation, rotation, scale) = ob.matrix_world.decompose() for vert in meshdata.verts: print("LOCAL CO",vert.co) # @による行列積で、global座標化 print("GLOBAL CO", matrix_world @ vert.co) # 指定の面数でオブジェクトを分離する def divide_mesh_object( obj_name ) -> bool: # 一旦、全てを選択解除 for ob in bpy.context.scene.objects: ob.select_set(False) ob = bpy.data.objects.get( obj_name ) if not ob: return False ob.select_set(True) bpy.context.view_layer.objects.active = ob bpy.ops.object.mode_set(mode='EDIT', toggle=False) meshdata = bmesh.from_edit_mesh(ob.data) # meshデータ取得 meshdata.select_mode = {'FACE'} # 面選択modeへ bpy.ops.mesh.select_all(action='DESELECT') # 一旦、選択解除し meshdata.select_flush_mode() # 辺/面の選択状態を更新 meshdata.verts.ensure_lookup_table() # 更にtable更新 # カウントを初期化 select_count = 0 max_select = 3 # https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMFace for face in meshdata.faces: if select_count >= max_select: face.select_set(False) continue face.select_set(True) select_count += 1 meshdata.select_flush_mode() # 辺/面の選択状態を更新 ob.data.update() # objectデータを更新 bpy.ops.mesh.separate(type='SELECTED') # 選択面を分離 bpy.ops.object.mode_set(mode='OBJECT', toggle=False) ob.select_set(False) def move_vertex(obj_name, new_pos): ob = bpy.context.scene.objects[ obj_name ] bpy.context.view_layer.objects.active = ob bpy.ops.object.mode_set(mode='EDIT', toggle=False) meshdata=bmesh.from_edit_mesh(ob.data) meshdata.select_mode = {'VERT'} # 頂点モード化 for v in meshdata.verts: v.co.x += new_pos[0] v.co.y += new_pos[1] v.co.z += new_pos[2] def copy_mesh_object(obj_name, new_pos): # object modeへ bpy.ops.object.mode_set(mode='OBJECT', toggle=False) for ob in bpy.context.scene.objects: #一旦、全選択を解除 ob.select_set(False) ob = bpy.context.scene.objects[ obj_name ] ob.select_set(True) # bpy.context.view_layer.objects.active = ob # オブジェクトをコピー bpy.ops.object.duplicate_move() bpy.ops.transform.translate(value=(0,0,-2)) ob.select_set(False) def clear_transform( obj_name ): for ob in bpy.context.scene.objects: #一旦、全選択を解除 ob.select_set(False) ob = bpy.context.scene.objects[ obj_name ] ob.select_set(True) bpy.context.view_layer.objects.active = ob bpy.ops.object.location_clear(clear_delta=False) bpy.ops.object.rotation_clear(clear_delta=False) bpy.ops.object.scale_clear(clear_delta=False) def transform_mesh_object(obj_name, new_pos, new_rot, new_scale): ob = bpy.context.scene.objects[ obj_name ] ob.location = new_pos ob.rotation_euler = new_rot ob.scale = new_scale return ob def chk_mesh_verts(obj_name): ob = bpy.data.objects[ obj_name ] ob.select_set(True) bpy.context.view_layer.objects.active = ob # 編集 modeへ bpy.ops.object.mode_set(mode='EDIT', toggle=False) # mesh dataを取得 meshdata = bmesh.from_edit_mesh(ob.data) # 頂点を走査する for vert in meshdata.verts: print(vert.co) def cleanup_mesh_object( obj_name ): for ob in bpy.context.scene.objects: # 一旦、全選択を解除 ob.select_set(False) ob = bpy.data.objects[obj_name] ob.select_set(True) bpy.context.view_layer.objects.active = ob # 編集 modeへ bpy.ops.object.mode_set(mode='EDIT', toggle=False) bpy.ops.mesh.select_all(action='SELECT') # 頂点を全選択 # 大きさ0を融解 bpy.ops.mesh.dissolve_degenerate(threshold=0.0001) bpy.ops.mesh.select_all(action='SELECT') # 再び頂点を全選択 # 孤立しているものを削除 bpy.ops.mesh.delete_loose(use_verts=True, use_edges=True, use_faces=False) bpy.ops.mesh.select_all(action='SELECT') # 再び頂点を全選択 # 重複頂点を削除 bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False) # object modeへ bpy.ops.object.mode_set(mode='OBJECT', toggle=False) return ob def union_all_objects(): for ob in bpy.context.scene.objects: if ob.type != 'MESH': ob.select_set(False) # 選択解除 continue ob.select_set(True) bpy.context.view_layer.objects.active = ob bpy.ops.object.join() def add_cube(new_name, new_pos): bpy.ops.mesh.primitive_cube_add( location = new_pos, rotation = (math.radians(0), math.radians(0), math.radians(0)) ) # 作成したobject参照を取得 obj = bpy.context.view_layer.objects.active obj.name = new_name # 名前変更 obj.scale = ( 1, 1, 1 ) # scale変更 obj.select_set(False) # 選択解除 return obj def remove_all_obj(): for col in bpy.data.collections: for item in col.objects: col.objects.unlink(item) bpy.data.objects.remove(item) for item in bpy.context.scene.collection.objects: bpy.context.scene.collection.objects.unlink(item) bpy.data.objects.remove(item) for item in bpy.data.meshes: bpy.data.meshes.remove(item) for item in bpy.data.materials: bpy.data.materials.remove(item) if __name__ == '__main__': main()