MRが楽しい

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

Blender2.8で利用可能なpythonスクリプトを作る その38(UVマップの座標移動)

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

選択中の頂点のUVマップの頂点を指定の位置に移動する

選択中の頂点のUVマップの頂点を指定の位置に移動します。
スクリプトではメッシュのY軸の端の頂点の座標移動とピン止めを行い、UVの再展開を行っています。
・move_UVmap_selected.py

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

# 選択中の頂点のUVマップの頂点を指定の位置に移動する
def move_UVmap_selected(arg_objectname="Default", arg_uvname="UVMap", arg_uvpos=[0.0, 0.0]) -> bool:
    """選択中の頂点のUVマップの頂点をピン止めする

    Keyword Arguments:
        arg_objectname {str} -- 指定オブジェクト名 (default: {"Default"})
        arg_uvname {str} -- 指定UVマップ名 (default: {"UVMap"})

    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

    # 元々の操作モードを記録する
    befmode = bpy.context.active_object.mode

    # 編集モードに移行する
    bpy.ops.object.mode_set(mode='EDIT', toggle=False)

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

    # UVレイヤーの参照を取得する
    # レイヤーアクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html#bmesh.types.BMLayerAccessLoop)
    targetuvlayer = bmeshdata.loops.layers.uv.get(arg_uvname)

    # 指定UVマップが存在するか確認する
    if targetuvlayer == None:
        # 指定UVマップが存在しない場合は処理しない
        return False

    # メッシュの面の参照からUVマップの頂点を編集する
    # 面の参照を取得する
    # 面アクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMFace)
    for face in bmeshdata.faces:
        # 要素の参照を取得する
        # 要素アクセスのマニュアル
        # (https://docs.blender.org/api/current/bmesh.types.html#bmesh.types.BMElemSeq)
        for loop in face.loops:
            # 頂点が選択中か否か判定する
            if loop.vert.select == True:
                # UVレイヤーの参照を取得する
                loop_uv = loop[targetuvlayer]

                # 頂点のUV情報を取得して座標を設定する
                # UV情報アクセスのマニュアル
                # (https://docs.blender.org/api/current/bmesh.types.html#bmesh.types.BMLoopUV)
                loop_uv.uv = arg_uvpos

    # メッシュ情報を更新する
    bmesh.update_edit_mesh(selectob.data)

    # 変更前のモードに移行する
    bpy.ops.object.mode_set(mode=befmode)

    return True

# 選択中の頂点のUVマップの頂点をピン止めする
def pin_UVmap_selected(arg_objectname="Default", arg_uvname="UVMap") -> bool:
    """選択中の頂点のUVマップの頂点をピン止めする

    Keyword Arguments:
        arg_objectname {str} -- 指定オブジェクト名 (default: {"Default"})
        arg_uvname {str} -- 指定UVマップ名 (default: {"UVMap"})

    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

    # 元々の操作モードを記録する
    befmode = bpy.context.active_object.mode

    # 編集モードに移行する
    bpy.ops.object.mode_set(mode='EDIT', toggle=False)

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

    # UVレイヤーの参照を取得する
    # レイヤーアクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html#bmesh.types.BMLayerAccessLoop)
    targetuvlayer = bmeshdata.loops.layers.uv.get(arg_uvname)

    # 指定UVマップが存在するか確認する
    if targetuvlayer == None:
        # 指定UVマップが存在しない場合は処理しない
        return False

    # メッシュの面の参照からUVマップの頂点を編集する
    # 面の参照を取得する
    # 面アクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMFace)
    for face in bmeshdata.faces:
        # 要素の参照を取得する
        # 要素アクセスのマニュアル
        # (https://docs.blender.org/api/current/bmesh.types.html#bmesh.types.BMElemSeq)
        for loop in face.loops:
            # 頂点が選択中か否か判定する
            if loop.vert.select == True:
                # UVレイヤーの参照を取得する
                loop_uv = loop[targetuvlayer]

                # 頂点のUV情報を取得してピンを設定する
                # UV情報アクセスのマニュアル
                # (https://docs.blender.org/api/current/bmesh.types.html#bmesh.types.BMLoopUV)
                loop_uv.pin_uv = True

    # メッシュ情報を更新する
    bmesh.update_edit_mesh(selectob.data)

    # 変更前のモードに移行する
    bpy.ops.object.mode_set(mode=befmode)

    return True

# Y軸座標が最大または最小の座標の頂点を選択する
def select_vert_Ytip(arg_objectname="Default", arg_uvname="UVMap", arg_max=False, arg_selectmargin=0.01) -> bool:
    """Y軸座標が最大または最小の座標の頂点を選択する

    Keyword Arguments:
        arg_objectname {str} -- 指定オブジェクト名 (default: {"Default"})
        arg_uvname {str} -- 指定UVマップ名 (default: {"UVMap"})
        arg_max {bool} -- 最大値の端か否か (default: {True})

    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

    # 元々の操作モードを記録する
    befmode = bpy.context.active_object.mode

    # 編集モードに移行する
    bpy.ops.object.mode_set(mode='EDIT', toggle=False)

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

    # 選択モードを頂点選択モードにする
    bmeshdata.select_mode = {'VERT'}

    # 頂点を走査する
    # 頂点アクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMVert)
    for vert in bmeshdata.verts:
        # 全ての頂点は一旦選択状態を解除する
        vert.select = False

    # Y座標の比較用変数を初期化
    Ytip_pos = 0.0
    Ytip_checked = False
    
    # 頂点を走査する
    # 頂点アクセスのマニュアル
    # (https://docs.blender.org/api/current/bmesh.types.html?highlight=bmedge#bmesh.types.BMVert)
    for vert in bmeshdata.verts:
        # 1つ目の座標は基準として取得する
        if Ytip_checked == False:
            # 座標の値とインデックスを取得する
            Ytip_pos = vert.co[1]
            Ytip_checked = True
        else:
            # Y軸座標が最大または最小の座標かチェックする
            if (vert.co[1] > Ytip_pos) == arg_max:
                # 座標の値とインデックスを取得する
                Ytip_pos = vert.co[1]

    # インデックス参照のため、テーブルを初期化する
    bmeshdata.verts.ensure_lookup_table()

    # 再び頂点を走査する
    for vert in bmeshdata.verts:
        # Y軸座標が最大または最小の座標を選択する
        if arg_max:
            if (vert.co[1] > (Ytip_pos - arg_selectmargin)):
                vert.select = True
        else:
            if ((Ytip_pos + arg_selectmargin) > vert.co[1]):
                vert.select = True

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

    # 変更前のモードに移行する
    bpy.ops.object.mode_set(mode=befmode)

    return True

# UV展開を実行する(デフォルト設定)
def project_UVmap_mesh(arg_objectname="Default") -> bool:
    """UV展開を実行する(デフォルト設定)

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

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

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

    # 元々の操作モードを記録する
    befmode = bpy.context.active_object.mode

    # 編集モードに移行する
    bpy.ops.object.mode_set(mode='EDIT', toggle=False)
   
    # 頂点を全選択した状態とする
    bpy.ops.mesh.select_all(action='SELECT')

    # デフォルト設定のUV展開を実行する
    bpy.ops.uv.unwrap()

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

    return

# 関数の実行例
# メッシュ上のY軸下端の頂点を選択する
select_vert_Ytip(arg_objectname="Sphere", arg_uvname="UVMap", arg_max=False)
# 対応するUVメッシュの頂点座標を移動する
move_UVmap_selected(arg_objectname="Sphere", arg_uvname="UVMap", arg_uvpos=[0.4, 0.0])
# 対応するUVメッシュの頂点座標をピン止めする
pin_UVmap_selected(arg_objectname="Sphere", arg_uvname="UVMap")
# メッシュ上のY軸上端の頂点を選択する
select_vert_Ytip(arg_objectname="Sphere", arg_uvname="UVMap", arg_max=True)
# 対応するUVメッシュの頂点座標を移動する
move_UVmap_selected(arg_objectname="Sphere", arg_uvname="UVMap", arg_uvpos=[0.6, 1.0])
# 対応するUVメッシュの頂点座標をピン止めする
pin_UVmap_selected(arg_objectname="Sphere", arg_uvname="UVMap")
# メッシュのUVマップを再展開する
project_UVmap_mesh(arg_objectname="Sphere")

f:id:bluebirdofoz:20200601021852j:plain