2017年7月1日土曜日

ボーンからメッシュ形状を作成するアドオン

Blenderでボーンのアニメーションを作るにあたって
簡易的な形状でレンダリング等、 ボーンを基準にメッシュが作成できればと思うことが多かったので作成してみました。

Blenderでは簡易形状の作成だけなら
メッシュオブジェクトでボーン状に線ポリゴンを作成して スキン(Skin)モデファイアをつけて
「アーマーチュアを作成」ボタンでアーマーチュアを作成する手順もあります

ただ 自分のやりたかった用途では操作の手順が煩雑な上に
関節部分の形状や ひとかたまりの形状しか作れない等のデメリットがありました

そこでボーン形状からのモデリングです
Blenderではアーマチュアの表示形式が選べて デフォルトは八面体ですが
今回はエンベロープ表示を利用します

Blenderのボーンはヘッドとテイルで構成されていて
画面の表示は球形のヘッドとテイルを表す球体と 間を結ぶボディ形状という形になっています

編集モードでこの球を選択してAlt+Sを押すと径を変更することができます
プロパティシェルフで大きさを数値指定することもできますが、ボーンを移動させた時に数値が元に戻ってしまう等、不具合があるようです

位置や大きさを調整することで普通にモデリングするよりも素早く人体っぽい感じの見た目のものを作れるかと思います
ただし、これはアーマチュアですのでレンダリングしても表示されないですし
モデリングのスナップ対象にすることもできません

そこで作ったのがこのアドオンです
bl_info = {
    "name": "activebone to mesh",
    "description": "形状をカメラ方向から投影",
    "author": "Yukimi",
    "version": (0,2),
    "blender": (2, 6, 0),
    "location": "Object",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"}
import bpy
import math
from mathutils import Vector, Matrix
class active_bone_to_mesh(bpy.types.Operator):
    bl_idname = "object.active_bone_to_mesh"
    bl_label = "選択ボーンからメッシュ"
    bl_description = "選択したボーンのエンベロープを元にカプセル形状を作成"
    bl_options = {'REGISTER', 'UNDO'}
    def execute(self, context):
        if context.mode == 'EDIT_ARMATURE':
            obj = context.active_object
            #グローバル座標への変換マトリクス
            matrix_world = obj.matrix_world
            #エディットモードの状態で情報を更新
            obj.update_from_editmode()
            bones = obj.data.bones
            for b in bones:
                if b.select:
                    head_radius = b.head_radius
                    tail_radius = b.tail_radius
                    distance = (b.tail_local -b.head_local).length
                    mat = matrix_world*b.matrix_local
                    (vertices,faces) = get_cupsule_base(head_radius, tail_radius, distance)
                    #座標の変換
                    vertices = [mat*Vector(v) for v in vertices]
                    name = b.name
                    #メッシュの作成
                    mesh_obj = add_mesh_from_data(name,vertices,faces)
                    #作成元のボーンのウエイトを付加
                    v_group = mesh_obj.vertex_groups.new(name)
                    v_index = list( range( len(vertices) ) )
                    v_group.add(v_index, 1.0, 'REPLACE')
                    #モデファイアの付加
                    mesh_obj.modifiers.new("Armature", 'ARMATURE')
                    mesh_obj.modifiers["Armature"].object = obj
        return {'RUNNING_MODAL'}
#選択したボーンからカプセル形状を作成
def get_cupsule_base(head_radius, tail_radius, distance, roat_div = 12):
    #平行断面の座標を計算
    r_pos = []
    #両端の球面の分割数
    spire_dev= 10
    delta_rad = math.pi/spire_dev
    #ヘッド側の球面上
    for i in range(spire_dev-1, spire_dev//2 , -1):
        t = delta_rad * float(i)
        pos = [head_radius *math.cos(t), head_radius *math.sin(t)]
        r_pos.append( pos )
    #ヘッドとテールの間を結ぶ線の区間
    #球を結ぶ区間の分割数
    span_div = 4
    P_1 = [0,head_radius]
    P_2 = [distance, tail_radius]
    for i in range(span_div):
        t = float(i)/ span_div
        pos_x = (1-t)*P_1[0] + t*P_2[0]
        pos_y = (1-t)*P_1[1] + t*P_2[1]
        r_pos.append( [pos_x, pos_y] )
    #テール側の球面上
    for i in range((spire_dev//2 -1), 0 , -1):
        t = delta_rad * float(i+1)
        pos = [tail_radius *math.cos(t) +distance, tail_radius *math.sin(t)]
        r_pos.append( pos )
    #回転座標の計算
    pos_list = []
    for r in range(roat_div):
        t = 2* r *math.pi/roat_div
        for p in r_pos:
            pos = [ p[1] *math.cos(t), p[0], p[1] *math.sin(t) ]
            pos_list.append(pos)
    face_list = []
    #面のデータを作成
    #軸方向の頂点数
    n_count = len(r_pos)
    #両端の面データを作成
    f = [i* n_count for i in range(roat_div)]
    face_list.append(f)
    f = [((i+1)* n_count-1) for i in reversed(range(roat_div))]
    face_list.append(f)
    #回転部分の面データの作成
    for r in range(roat_div-1):
        for t in range(n_count-1):
            #面の頂点id
            f = [r*n_count + t, r*n_count + t+1, (r+1)*n_count +t +1 , (r+1)*n_count +t]
            face_list.append(f)
    #回転部分の面を閉じる列の面データ
    for t in range(n_count-1):
        f = [(roat_div-1)*n_count + t, (roat_div-1)*n_count + t+1, t +1, t ]
        face_list.append(f)
    return(pos_list, face_list)
#データからメッシュを作成
def add_mesh_from_data(name,vertices,faces):
    mesh = bpy.data.meshes.new(name)
    obj = bpy.data.objects.new(name, mesh)
    bpy.context.scene.objects.link(obj)
    mesh.from_pydata(vertices, [], faces)
    mesh.update()
    return( obj )

################################################
#メニュー項目の設定
def menu_funk(self, context):
    self.layout.operator("object.active_bone_to_mesh")
 # アドオン有効化時の処理
def register():
    bpy.utils.register_module(__name__)
    bpy.types.INFO_MT_edit_armature_add.append(menu_funk)
# アドオン無効化時の処理
def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.INFO_MT_edit_armature_add.remove(menu_funk)
################################################

アーマーチュアの編集モードのメニューの「追加」の項目に「選択ボーンからメッシュ」
という項目が追加されます
ボーン一つ一形状でヘッド テイルの設定に合わせた半球とそれを結ぶ形状を作り
元ボーンに合わせた頂点ウエイトやアーマチュア変形のモデファイアを付加してあります