MRが楽しい

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

Blender2.8で利用可能なpythonスクリプトを作る その36(メッシュ頂点とUVマップ頂点の対応)

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

メッシュ頂点のXY座標を元にUVマップのUV座標を変更する

メッシュ頂点のXY座標を元にUVマップのUV座標を変更します。
指定のUVマップが存在しない場合は新規作成します。
オブジェクトの中心点とサイズを元にUVマップに収まるよう調整します。
・project_UVmap_xyposh.py

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

# オブジェクトのXY軸座標を元にUVマップを設定する
def project_UVmap_xypos(arg_objectname="Default", arg_uvname="UVMap") -> bool:
    """オブジェクトのXY軸座標を元に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マップが存在しない場合は新規作成する
        targetuvlayer = bmeshdata.loops.layers.uv.new(arg_uvname)

    # オブジェクトのバウンドボックスから中心点を計算する
    target_centerpos = get_object_center(arg_objectname=arg_objectname)

    # オブジェクトのサイズを取得する
    target_dimension = selectob.dimensions

    # X軸とY軸の長い方の辺の長さを取得する
    boundsize = 0.0
    if (target_dimension[0] > target_dimension[1]):
        boundsize = target_dimension[0]
    else:
        boundsize = target_dimension[1]
    
    # メッシュの面の参照から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:
            # UVレイヤーの参照を取得する
            loop_uv = loop[targetuvlayer]

            # メッシュ頂点のXY座標を取得する
            uvpos_u = loop.vert.co.x
            uvpos_v = loop.vert.co.y

            # 中心点を使ってUV座標における原点位置(0,0)に補正する
            uvpos_u = uvpos_u - (target_centerpos[0] - (boundsize / 2.0))
            uvpos_v = uvpos_v - (target_centerpos[1] - (boundsize / 2.0))

            # UV座標の大きさ(1,1)に収まるよう調整する
            uvpos_u = uvpos_u / boundsize
            uvpos_v = uvpos_v / boundsize

            # 頂点のUV情報を取得する
            # UV情報アクセスのマニュアル
            # (https://docs.blender.org/api/current/bmesh.types.html#bmesh.types.BMLoopUV)
            loop_uv.uv[0] = uvpos_u
            loop_uv.uv[1] = uvpos_v

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

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

    return True

# オブジェクトのバウンドボックスから中心点を計算する
def get_object_center(arg_objectname="Default") -> list:
    """オブジェクトのバウンドボックスから中心点を計算する

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

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

    # 指定オブジェクトが存在するか確認する
    if selectob == None:
        # 指定オブジェクトが存在しない場合は処理しない
        return [0.0, 0.0, 0.0]
    
    # 指定オブジェクトの境界情報を取得する
    # オブジェクトのインタフェース
    # (https://docs.blender.org/api/current/bpy.types.Object.html)
    target_boundbox = selectob.bound_box

    # 変数の初期化
    vertcount = 0
    center_x = 0.0
    center_y = 0.0
    center_z = 0.0

    # 頂点座標の加算
    for vert_boundbox in target_boundbox:
        center_x += vert_boundbox[0]
        center_y += vert_boundbox[1]
        center_z += vert_boundbox[2]
        vertcount += 1

    # 中点の計算
    center_x = center_x / vertcount
    center_y = center_y / vertcount
    center_z = center_z / vertcount

    return [center_x, center_y, center_z]

# 関数の実行例
project_UVmap_xypos(arg_objectname="Sphere", arg_uvname="UVMap")

f:id:bluebirdofoz:20200530221651j:plain