2017年8月22日火曜日

グリースペンシルからメッシュ

画像を基準にメッシュを作りたくて作成したアドオンを公開します。

画像のアルファから境界線を判別するものも作成したのですが
今回はグリースペンシルで囲った範囲にメッシュを作るものになります


メッシュ作成部分はCOA Toolsアドオンのコードを参考に細かい三角メッシュで埋めるようになっています

bl_info = {
    "name": "grease_pencil storoke to mesh",
    "description": "UV/画像エディッタの上のグリースペンシルのストロークからメッシュの作成",
    "author": "Yukimi",
    "version": (0,2),
    "blender": (2, 6, 0),
    "location": "Object",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"}

import bpy
import bmesh
def triangle_fill(bm):
    #三角面作成
    triangle_fill = bmesh.ops.triangle_fill(bm,edges=bm.edges,use_beauty=True)
    if triangle_fill["geom"] == []:
        return False
    else:
        return True

def average_edge_cuts(bm,cuts=1):
    ### collapse short edges
    edges_len_average = 0
    edges_count = 0
    shortest_edge = 10000
    for edge in bm.edges:
        if True:#edge.is_boundary:
            edges_count += 1
            length = edge.calc_length()
            edges_len_average += length
            if length < shortest_edge:
                shortest_edge = length
    edges_len_average = edges_len_average/edges_count

    subdivide_edges = []
    for edge in bm.edges:
        cut_count = int(edge.calc_length()/shortest_edge*0.5)*cuts
        if cut_count < 0:
            cut_count = 0
        if not edge.is_boundary:
            subdivide_edges.append([edge,cut_count])
    for edge in subdivide_edges:
        bmesh.ops.subdivide_edges(bm,edges=[edge[0]],cuts=edge[1])

def smooth_verts(bm):
    ### smooth verts
    smooth_verts = []
    for vert in bm.verts:
        if not vert.is_boundary:
            smooth_verts.append(vert)
    for i in range(50):   
        bmesh.ops.smooth_vert(bm,verts=smooth_verts,factor=1.0,use_axis_x=True,use_axis_y=True,use_axis_z=True)    

def bm_pos_to_uv(bm):
    ##UVの設定
    uv_list = [[vert.co[0], vert.co[1]] for vert in bm.verts]
    uv_layer = bm.loops.layers.uv.verify()
    bm.faces.layers.tex.verify()
    for bm_face in bm.faces:
        for v in bm_face.loops:
            id = v.vert.index
            v[uv_layer].uv = uv_list[id]

def stroke_to_mesh(stroke, img):
    me = bpy.data.meshes.new('gpenMesh') 
    #オブジェクトの作成
    obj = bpy.data.objects.new(img.name, me) 
    #オブジェクトをシーンにリンク
    bpy.context.scene.objects.link(obj)
    bm = bmesh.new() # BMeshから空のメッシュを作成
    verts = []
    #bmesh頂点データの作成
    for p in stroke.points:
        pos = p.co
        v = bm.verts.new( [pos[0], pos[1], 0.0] )
        verts.append(v)
    bm.verts.index_update()
    #エッジデータの作成
    for i in range(len(verts)-1):
        bm.edges.new( (verts[i], verts[i +1]) )
    bm.edges.new((verts[-1], verts[0]))
    #メッシュで埋める
    fill_ok = triangle_fill(bm)#三角面作成
    if fill_ok:
        average_edge_cuts(bm)
        bm.verts.index_update()
        bmesh.ops.triangulate(bm,faces=bm.faces)
        smooth_verts(bm) #頂点のスムーズ
        bm_pos_to_uv(bm) #UVの設定
    bm.to_mesh(me) #メッシュの作成
    # 画像をUVにリンク
    for i in range( len(obj.data.polygons) ):
        obj.data.uv_textures[0].data[i].image = img
    return( obj )
############################################
class GpenToMesh(bpy.types.Operator):
    '''add Roop  '''
    bl_idname = "action.gpen2mesh"
    bl_label = "make mesh from grease_pencil"
    def execute(self, context):
        #UV/Imageエディッタで使用中のGreasePencil
        gpen_strokes = context.active_gpencil_layer.active_frame.strokes
        #UV/Imageエディッタで表示中の画像
        img = context.edit_image
        for stroke in gpen_strokes:
            stroke_to_mesh(stroke,img)
        return {'FINISHED'}
#メニューへの登録
def mesh_menu_func(self, context):
    self.layout.operator("action.gpen2mesh", 
        text="GreasePencilからメッシュ作成" )
        
def register():
    bpy.utils.register_module(__name__)
    bpy.types.IMAGE_MT_image.prepend(mesh_menu_func)
 
def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.INFO_MT_file_import.remove(mesh_menu_func)
 
if __name__ == "__main__":
    #unregister()
    register()

############################################

コードを実行するとかアドオンとして登録すると
UV/画像エディッタの画像メニューに「GreasePencilからメッシュ作成」という項目が登録されます

今回のコードでは 上記理由でUV/画像エディッタで囲った範囲を元に
XY平面上の0から1.0の範囲にメッシュを作成するようになっていますが
少しコードを変更すれば 3Dビューの方で描いたグリースペンシルに沿ってメッシュを作成することもできるかと思います
グリースペンシルの頂点のデータをそのまま使うため
ドローモードで描いたストロークでは細かくなりすぎるため多角形モードで描いた方がいいかもしれません

いかがでしょうか