end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

blender python で いろいろ処理

詳細は、script内のcommentの通り

以下のurlも参考になります

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()