MRが楽しい

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

Blender3.0で利用可能なpythonスクリプトを作る その95(頂点グループの割合を考慮してオブジェクトをリダクションする)

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

頂点グループの割合を考慮してオブジェクトをリダクションする

先日作成した以下の「頂点グループを指定してポリゴン数削減モディファイアを適用する」には、頂点グループ内の比率ではなく、オブジェクト全体に対しての削減比率が適用される問題がありました。
bluebirdofoz.hatenablog.com

今回は頂点グループ内の頂点数と、オブジェクト全体の頂点数から削減比率を再計算するスクリプトを作成します。
オブジェクト全体の頂点数は以下の Mesh データの vertices から計算できます。
docs.blender.org

また頂点グループに含まれる頂点の数は以下の記事で紹介した手法で計算できます。
bluebirdofoz.hatenablog.com

サンプルスクリプト

頂点グループを指定するポリゴン数削減モディファイアを適用するサンプルスクリプトを作成しました。
指定した削減比率は選択していた頂点数の比率に合わせて再計算されます。
・Script_decimate_vertexgroup.py

# bpyインポート
import bpy

def apply_decimate_vertexgroup(
    arg_object:bpy.types.Object,
    arg_vertexgroup_name:str,
    arg_decimateratio:float) -> bool:
    """頂点グループを指定してポリゴン数削減モディファイアを適用する

    Keyword Arguments:
        arg_object (bpy.types.Object): 指定オブジェクト
        arg_vertexgroup_name {str} -- 指定の頂点グループ名
        arg_decimateratio {float} -- 削減比率

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

    # 指定オブジェクトに指定の頂点グループが含まれるかチェックする
    check_vertexgroup = arg_object.vertex_groups.get(arg_vertexgroup_name)
    if check_vertexgroup == None:
        return False
    
    # 指定オブジェクトがメッシュか確認する
    if arg_object.type != 'MESH':
        # メッシュが存在しない場合は処理しない
        return False

    # 変更オブジェクトをアクティブに変更する
    bpy.context.view_layer.objects.active = arg_object
    
    # 「ポリゴン数削減」モディファイアを追加する
    # モディファイア追加の種類とマニュアル
    # (https://docs.blender.org/api/current/bpy.ops.object.html#bpy.ops.object.gpencil_modifier_add)
    # ポリゴン数削減モディファイアのインタフェース
    # (https://docs.blender.org/api/current/bpy.types.DecimateModifier.html)
    bpy.ops.object.modifier_add(type='DECIMATE')

    # 追加されたモディファイアを取得する
    decimate_modifier = arg_object.modifiers[-1]

    # 削減のタイプを COLLAPSE に指定する
    decimate_modifier.decimate_type = 'COLLAPSE'

    # 削減の比率を設定する
    decimate_modifier.ratio = arg_decimateratio

    # 頂点グループを指定する
    decimate_modifier.vertex_group = arg_vertexgroup_name

    # 「ポリゴン数削減」モディファイアを適用する
    bpy.ops.object.modifier_apply(modifier=decimate_modifier.name)
    
    # 実行成否を返却する
    return True

def calculate_decimatefactor_onvertexgourp(
    arg_object:bpy.types.Object,
    arg_vertexgroup_name:str,
    arg_decimateratio:float) -> float:
    """頂点グループの頂点数に対応する削減比率を再計算する

    Keyword Arguments:
        arg_object {bpy.types.Object} -- 指定オブジェクト
        arg_vertexgroup_name {str} -- 指定の頂点グループ名
        arg_decimateratio {float} -- 削減比率

    Returns:
        float -- 再計算後の削減比率
    """

    # 指定オブジェクトに指定の頂点グループが含まれるかチェックする
    check_vertexgroup = arg_object.vertex_groups.get(arg_vertexgroup_name)
    if check_vertexgroup == None:
        return arg_decimateratio
    
    # 指定オブジェクトがメッシュか確認する
    if arg_object.type != 'MESH':
        # メッシュが存在しない場合は処理しない
        return arg_decimateratio

    # 頂点グループの参照から頂点グループに所属している頂点数を算出つする
    vertexgroup_count = count_weight_vertexgroup(arg_object, arg_vertexgroup_name)
    # 全体の頂点数を確認する
    vertex_count = count_data_vertex(arg_object)
    # 頂点グループの割合を算出する
    vertexgroup_ratio = vertexgroup_count / vertex_count
    # 頂点グループの割合を考慮して削減比率を再計算する
    vertexgroup_decimateratio = 1.0 - ((1.0 - arg_decimateratio) * vertexgroup_ratio)

    return vertexgroup_decimateratio

def count_data_vertex(arg_object:bpy.types.Object) -> int:
    """指定のオブジェクトの頂点の数を取得する

    Keyword Arguments:
        arg_object {bpy.types.Object} -- 指定オブジェクト

    Returns:
        int -- 頂点数
    """

    # データに保持されている頂点の数を返却する
    return len(arg_object.data.vertices)

def count_weight_vertexgroup(
    arg_object:bpy.types.Object, arg_vertexgroup_name:str) -> int:
    """指定の頂点グループに所属している頂点の数を取得する(頂点グループの参照から算出)

    Keyword Arguments:
        arg_object {bpy.types.Object} -- 指定オブジェクト
        arg_vertexgroup_name {str} -- 指定の頂点グループ名

    Returns:
        int -- 頂点グループに含まれる頂点数
    """

    # 指定オブジェクトに指定の頂点グループが含まれるかチェックする
    check_vertexgroup = arg_object.vertex_groups.get(arg_vertexgroup_name)
    if check_vertexgroup == None:
        return 0
    
    # カウント用変数
    result_count = 0

    # 指定の頂点グループについて頂点グループリストでのインデックス番号を取得する
    check_vertexgroup_index = check_vertexgroup.index

    # 指定オブジェクトの全頂点を走査する
    for vert in arg_object.data.vertices:
        try:
            # ウェイト情報を参照できるならカウントアップする
            check_vertexgroup.weight(vert.index)
            result_count = result_count + 1
        except RuntimeError:
            # 頂点グループに所属していない場合はエラーが発生するのでパスする
            pass

    # カウント結果を返却する
    return result_count

# オブジェクトモードへの移行
# モード切替のマニュアル
# (https://docs.blender.org/api/current/bpy.ops.object.html#bpy.ops.object.mode_set)
def set_mode_object() -> bool:
    """オブジェクトモードへの移行

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

    # オブジェクトモードに移行する
    # モード切替のマニュアル
    # (https://docs.blender.org/api/current/bpy.ops.object.html#bpy.ops.object.mode_set)
    # mode:OBJECT オブジェクトモードに切り替え
    # toggle:True の場合、既に編集モードの時、オブジェクトモードに戻る
    bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

    return True

# 選択中の頂点から新規頂点グループを作成して頂点グループを取得する
def make_vertexgroup() -> bpy.types.VertexGroup:
    """選択中の頂点から新規頂点グループを作成して頂点グループを取得する

    Keyword Arguments:

    Returns:
        bpy.types.VertexGroup -- 新規頂点グループ
    """

    # 現在のモードが「編集モード(EDIT_MESH)」かチェックする
    if check_viewmode('EDIT_MESH') == False :
        return ""

    # 編集中のアクティブオブジェクトを取得する
    active_object = bpy.context.view_layer.objects.active

    # 選択中の頂点に新規頂点グループを割り当てる
    bpy.ops.object.vertex_group_assign_new()

    # 作成した頂点グループのインデックス番号を取得する
    make_index = active_object.vertex_groups.active_index

    # 作成した頂点グループの頂点グループを返却する
    return active_object.vertex_groups[make_index]

# 現在のモードが指定のモードかチェックする
def check_viewmode(arg_checktype:str) -> bool:
    """現在のモードが指定のモードかチェックする

    Keyword Arguments:
        arg_checktype {str} -- 比較するモード名

    Returns:
        str -- 現在のモード
    """

    # 現在のモードをチェックする
    # (https://docs.blender.org/api/current/bpy.context.html#bpy.context.mode)
    modetype = bpy.context.mode

    return (arg_checktype == modetype)

# 関数の実行例
# 頂点の削減比率を指定する
decimate_ratio = 0.5
# 選択中の頂点から新規頂点グループを作成して頂点グループを取得する
made_vertexgroup = make_vertexgroup()
# 頂点グループの頂点数を考慮した削減比率を再計算する
vertexgroup_decimeate_ratio = calculate_decimatefactor_onvertexgourp(
    bpy.context.view_layer.objects.active, made_vertexgroup.name, decimate_ratio
)
# モディファイアを設定するためオブジェクトモードに切り替える
set_mode_object()
# リダクションを行う
apply_decimate_vertexgroup(bpy.context.view_layer.objects.active, made_vertexgroup.name, vertexgroup_decimeate_ratio)
# 再計算した削減比率をコンソールに表示する
print("vertexgroup_decimeate_ratio : " + str(vertexgroup_decimeate_ratio))

・実行前
f:id:bluebirdofoz:20220104231403j:plain
・実行後
f:id:bluebirdofoz:20220104231412j:plain