MRが楽しい

MRやVRについて学習したことを書き残す

Blender2.8で利用可能なpythonスクリプトを作る その33(面の法線を元にした辺の設定)

本日は Blender の技術調査枠です。
Blender2.8で利用可能なpythonスクリプトを作ります。

面の法線を元にした辺の設定

Z軸の法線方向が上向きの面と下向きの面の境界の辺にシーム/シャープ/クリース/ベベルを設定します。
最初に面の向きを元に面を選択し、辺に接続された一方の面のみ選択されている場合、境界と判断して辺を選択します。
その選択した辺に対してシーム/シャープ/クリース/ベベルの設定を行います。
・apply_mark_edges_Znormal.py

# bpyインポート
import bpy
# メッシュ操作のため
import bmesh

# Z軸の法線方向が上向きの面と下向きの面の境界の辺にシーム/シャープ/クリース/ベベルを設定する
def mark_edges_Znormal(arg_objectname="Default",
  arg_seam=False, arg_sharp=False, arg_crease=False, arg_bevel=False) -> bool:
    """Z軸の法線方向が上向きの面と下向きの面の境界の辺にシーム/シャープ/クリース/ベベルを設定する

    Keyword Arguments:
        arg_objectname {str} -- [description] (default: {"Default"})
        arg_seam {bool} -- シームの設定有無 (default: {False})
        arg_sharp {bool} -- シャープの設定有無 (default: {False})
        arg_crease {bool} -- クリースの設定有無 (default: {False})
        arg_bevel {bool} -- ベベルの設定有無 (default: {False})

    Returns:
        bool -- 実行正否
    """

    # 指定オブジェクトを取得する
    # (get関数は対象が存在しない場合 None が返る)
    selectob = bpy.data.objects.get(arg_objectname)

    # 指定オブジェクトが存在するか確認する
    if selectob == None:
        # 指定オブジェクトが存在しない場合は処理しない
        return False
    
    # オブジェクトがメッシュであるか確認する
    if selectob.type != 'MESH':
        # 指定オブジェクトがメッシュでない場合は処理しない
        return False

    # 不要なオブジェクトを選択しないように
    # 全てのオブジェクトを走査する
    for ob in bpy.context.scene.objects:
        # 非選択状態に設定する
        ob.select_set(False)

    # 指定のオブジェクトのみを選択状態にする
    selectob.select_set(True)

    # 対象オブジェクトをアクティブに変更する
    bpy.context.view_layer.objects.active = selectob

    # 編集モードに移行する
    # モード切替のマニュアル
    # (https://docs.blender.org/api/current/bpy.ops.object.html#bpy.ops.object.mode_set)
    bpy.ops.object.mode_set(mode='EDIT', toggle=False)
    
    # メッシュデータを取得する
    # メッシュアクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMesh)
    meshdata = bmesh.from_edit_mesh(selectob.data)

    # 選択モードを面選択モードにする
    meshdata.select_mode = {'FACE'}
    
    # 一度選択を全てクリアする
    bpy.ops.mesh.select_all(action='DESELECT')
    
    # 辺または面の選択状態を更新する
    meshdata.select_flush_mode()
    
    # 辺または面を操作する場合はテーブルを更新する
    meshdata.verts.ensure_lookup_table()

    # 辺を走査する
    # 面アクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMFace)
    for face in meshdata.faces:
        # 指定の数の面を選択する
        if face.normal[2] > 0.0:
            # 面を選択状態にする
            face.select_set(True)
        else:
            # 面を非選択状態にする
            face.select_set(False)

    # メッシュデータを取得する
    # メッシュアクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMesh)
    meshdata = bmesh.from_edit_mesh(selectob.data)

    # 選択モードを面選択モードにする
    meshdata.select_mode = {'EDGE'}
    
    # 辺または面の選択状態を更新する
    meshdata.select_flush_mode()

    # 辺または面を操作する場合はテーブルを更新する
    meshdata.verts.ensure_lookup_table()

    # 辺を走査する
    # 辺アクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMEdge)
    for edge in meshdata.edges:
        # 辺に接続された面を取得する
        faces = edge.link_faces
        # 接続された面が2つであることを確認する
        if len(faces) == 2:
            # 選択数をチェックする
            selectcount = 0

            # 各面を走査する
            for face in faces:
                if face.select == True:
                    # 選択状態の面をカウントする
                    selectcount += 1
            
            # 選択状態の面の数を確認する
            if selectcount == 1:
                # 1つ(片方)のみ選択状態であれば辺を選択状態にする
                edge.select_set(True)
            else:
                # 辺を非選択状態にする
                edge.select_set(False)
        else:
            # 辺を非選択状態にする
            edge.select_set(False)

    # 辺または面の選択状態を更新する
    meshdata.select_flush_mode()

    # オブジェクトデータを更新する
    selectob.data.update()

    # 現在選択中の辺に対して設定を行う
    # シームの設定を行う
    if arg_seam == True:
        bpy.ops.mesh.mark_seam(clear=False)

    # シャープの設定を行う
    if arg_sharp == True:
        bpy.ops.mesh.mark_sharp()
    
    # クリースの設定を行う
    if arg_crease == True:
        bpy.ops.transform.edge_crease(value=1.0)
    
    # ベベルの設定を行う
    if arg_bevel == True:
        bpy.ops.transform.edge_bevelweight(value=1.0)

    # オブジェクトモードに移行する
    bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

    return True


# 関数の実行例
mark_edges_Znormal(arg_objectname="Target", arg_seam=True, arg_sharp=True, arg_crease=True, arg_bevel=True)

f:id:bluebirdofoz:20200524221743j:plain

面の法線を元にした辺の分離

Z軸の法線方向が上向きの面と下向きの面の境界の辺にシャープを設定します。
その後、[辺分離]モディファイアを[シャープな辺]に対して適用して辺を分離します。
・apply_apply_split_sharpedge.py

# bpyインポート
import bpy
# メッシュ操作のため
import bmesh

# Z軸の法線方向が上向きの面と下向きの面の境界の辺にシーム/シャープ/クリース/ベベルを設定する
def mark_edges_Znormal(arg_objectname="Default",
  arg_seam=False, arg_sharp=False, arg_crease=False, arg_bevel=False) -> bool:
    """Z軸の法線方向が上向きの面と下向きの面の境界の辺にシーム/シャープ/クリース/ベベルを設定する

    Keyword Arguments:
        arg_objectname {str} -- [description] (default: {"Default"})
        arg_seam {bool} -- シームの設定有無 (default: {False})
        arg_sharp {bool} -- シャープの設定有無 (default: {False})
        arg_crease {bool} -- クリースの設定有無 (default: {False})
        arg_bevel {bool} -- ベベルの設定有無 (default: {False})

    Returns:
        bool -- 実行正否
    """

    # 指定オブジェクトを取得する
    # (get関数は対象が存在しない場合 None が返る)
    selectob = bpy.data.objects.get(arg_objectname)

    # 指定オブジェクトが存在するか確認する
    if selectob == None:
        # 指定オブジェクトが存在しない場合は処理しない
        return False
    
    # オブジェクトがメッシュであるか確認する
    if selectob.type != 'MESH':
        # 指定オブジェクトがメッシュでない場合は処理しない
        return False

    # 不要なオブジェクトを選択しないように
    # 全てのオブジェクトを走査する
    for ob in bpy.context.scene.objects:
        # 非選択状態に設定する
        ob.select_set(False)

    # 指定のオブジェクトのみを選択状態にする
    selectob.select_set(True)

    # 対象オブジェクトをアクティブに変更する
    bpy.context.view_layer.objects.active = selectob

    # 編集モードに移行する
    # モード切替のマニュアル
    # (https://docs.blender.org/api/current/bpy.ops.object.html#bpy.ops.object.mode_set)
    bpy.ops.object.mode_set(mode='EDIT', toggle=False)
    
    # メッシュデータを取得する
    # メッシュアクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMesh)
    meshdata = bmesh.from_edit_mesh(selectob.data)

    # 選択モードを面選択モードにする
    meshdata.select_mode = {'FACE'}
    
    # 一度選択を全てクリアする
    bpy.ops.mesh.select_all(action='DESELECT')
    
    # 辺または面の選択状態を更新する
    meshdata.select_flush_mode()
    
    # 辺または面を操作する場合はテーブルを更新する
    meshdata.verts.ensure_lookup_table()

    # 辺を走査する
    # 面アクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMFace)
    for face in meshdata.faces:
        # 指定の数の面を選択する
        if face.normal[2] > 0.0:
            # 面を選択状態にする
            face.select_set(True)
        else:
            # 面を非選択状態にする
            face.select_set(False)

    # メッシュデータを取得する
    # メッシュアクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMesh)
    meshdata = bmesh.from_edit_mesh(selectob.data)

    # 選択モードを面選択モードにする
    meshdata.select_mode = {'EDGE'}
    
    # 辺または面の選択状態を更新する
    meshdata.select_flush_mode()

    # 辺または面を操作する場合はテーブルを更新する
    meshdata.verts.ensure_lookup_table()

    # 辺を走査する
    # 辺アクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMEdge)
    for edge in meshdata.edges:
        # 辺に接続された面を取得する
        faces = edge.link_faces
        # 接続された面が2つであることを確認する
        if len(faces) == 2:
            # 選択数をチェックする
            selectcount = 0

            # 各面を走査する
            for face in faces:
                if face.select == True:
                    # 選択状態の面をカウントする
                    selectcount += 1
            
            # 選択状態の面の数を確認する
            if selectcount == 1:
                # 1つ(片方)のみ選択状態であれば辺を選択状態にする
                edge.select_set(True)
            else:
                # 辺を非選択状態にする
                edge.select_set(False)
        else:
            # 辺を非選択状態にする
            edge.select_set(False)

    # 辺または面の選択状態を更新する
    meshdata.select_flush_mode()

    # オブジェクトデータを更新する
    selectob.data.update()

    # 現在選択中の辺に対して設定を行う
    # シームの設定を行う
    if arg_seam == True:
        bpy.ops.mesh.mark_seam(clear=False)

    # シャープの設定を行う
    if arg_sharp == True:
        bpy.ops.mesh.mark_sharp()
    
    # クリースの設定を行う
    if arg_crease == True:
        bpy.ops.transform.edge_crease(value=1.0)
    
    # ベベルの設定を行う
    if arg_bevel == True:
        bpy.ops.transform.edge_bevelweight(value=1.0)

    # オブジェクトモードに移行する
    bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

    return True

# シャープ辺を対象とする辺分離モディファイアを適用する
def apply_split_sharpedge(arg_objectname="Default") -> bool:
    """対象オブジェクトにブーリアンモディファイアの差分を適用する

    Keyword Arguments:
        arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})

    Returns:
        bool -- 実行正否
    """
    
    # 指定オブジェクトを取得する
    # (get関数は対象が存在しない場合 None が返る)
    selectob = bpy.data.objects.get(arg_objectname)

    # 指定オブジェクトが存在するか確認する
    if selectob == None:
        # 指定オブジェクトが存在しない場合は処理しない
        return False
    
    # オブジェクトがメッシュであるか確認する
    if selectob.type != 'MESH':
        # 指定オブジェクトがメッシュでない場合は処理しない
        return False

    # 指定オブジェクトをアクティブに変更する
    bpy.context.view_layer.objects.active = selectob

    # 「辺分離」モディファイアを追加する
    # 辺分離モディファイアのインタフェース
    # (https://docs.blender.org/api/current/bpy.types.EdgeSplitModifier.html)
    bpy.ops.object.modifier_add(type='EDGE_SPLIT')
  
    # 作成モディファイアを取得する
    split_modifier = selectob.modifiers[-1]
  
    # シャープ辺のみで分離
    split_modifier.use_edge_angle = False
    split_modifier.use_edge_sharp = True

    # 「辺分離」モディファイアを適用する
    bpy.ops.object.modifier_apply(apply_as='DATA',modifier=split_modifier.name)

    return True

# 関数の実行例
mark_edges_Znormal(arg_objectname="Target", arg_sharp=True)
apply_split_sharpedge(arg_objectname="Target")

f:id:bluebirdofoz:20200524221759j:plain