2012年6月28日木曜日

blender - 2.6 - pythonで3DViewにお絵かきin3Dその1

画面と平行な適当な3D座標を返してくれる関数を発見したので、
フリーハンドで2Dに描いたものを渡してやって、ご覧のとおり。

glUnProjectのほうは上手くいかない…。
昔はBpyWindowにmouseViewRayとかいう関数があったらしいので、それ参考にするか…

まぁ現状でも結構満足している。これにAn algorithm for automatically fitting digitized curves実装したらどの程度速度出るだろうか…。
# Blender 3DViewport Line Drawer
# author: uimac
# data: 2012/6/28

import bpy
import bgl
import bpy_extras
from bpy_extras import *
import mathutils
from mathutils import *

class LineDrawer:
    
    pre_x = 0
    pre_y = 0
    is_mouse_pressed = 0
    
    # [(x0, y0), (x1, y1), ...]
    point_list = []
    
    # [(world_x_0, world_y_0, world_z_0), ...]
    point_list_3d = []
    
    # [ point_list_1, point_list_2, ... ]
    stroke_list = []
    
    # [ point_list_3d_1, point_list_3d_2, ... ]
    stroke_list_3d = []
    
    viewport = None
    
    line_object = None
    line_mesh = None
    
    def __init__(self):
        self.point_list = []
        self.stroke_list = []
        self.point_list_3d = []
        self.stroke_list_3d = []
        self.viewport = None
        self.pre_x = 0
        self.pre_y = 0
        
    def init_buffers(self, context):
        if self.viewport == None:
            self.viewport = bgl.Buffer(bgl.GL_INT, 4)
            
        if self.line_object == None:
            self.line_mesh = bpy.data.meshes.new("line_mesh")
            self.line_object = bpy.data.objects.new("line", self.line_mesh)
            self.line_object.data = self.line_mesh
            context.scene.objects.link(self.line_object) 
            
    def dispose(self):
        self.__init__()
    
    def mouse(self, context, event):
        x = event.mouse_x
        y = event.mouse_y
                
        if event.type == 'LEFTMOUSE':
            if event.value == 'PRESS':
                self.is_mouse_pressed = True
                self.pre_x = x
                self.pre_y = y
                
            elif event.value == 'RELEASE':
                self.is_mouse_pressed = False
                self.stroke_list.append(self.point_list)
                self.stroke_list_3d.append(self.point_list_3d)
                self.point_list = []
                self.point_list_3d = []
                self.convert_stroke_to_mesh()
                
        if event.type == 'MOUSEMOVE':
            if self.is_mouse_pressed:
                point_list = self.point_list
                point_list.append((x,y))
                
                # tekitou hokan
                pointlen = len(point_list)
                if pointlen >= 3:
                    last = pointlen - 1
                    p0 = point_list[last-2]
                    p1 = point_list[last-1]
                    p2 = point_list[last]
                    p = ( \
                    (((p0[0] + p2[0]) >> 1) + p1[0]) >> 1,
                    (((p0[1] + p2[1]) >> 1) + p1[1]) >> 1)
                    
                    self.point_list[last-1] = p
                    p3d = self.unproject_parallel(context, p[0], p[1])
                    self.point_list_3d.append(p3d)
                
        self.pre_x = x
        self.pre_y = y

    def unproject_parallel(self, context, x, y):
        px = x - self.viewport[0]
        py = y - self.viewport[1]
        
        coord = (px, py)
        region = context.region
        region_3d = context.space_data.region_3d
        
        vec = bpy_extras.view3d_utils.region_2d_to_vector_3d(\
                region, region_3d, coord)
                
        loc = bpy_extras.view3d_utils.region_2d_to_location_3d(\
                region, region_3d, coord, vec)
        
        return (loc.x, loc.y, loc.z)

    def draw_line(self, x0, y0, x1, y1):
        bgl.glBegin(bgl.GL_LINES)
        bgl.glVertex2i(x0,y0)
        bgl.glVertex2i(x1,y1)
        bgl.glEnd()
    
    def draw_lines(self, context, event, viewport):
        
        if len(self.stroke_list) > 0:
            for stroke in self.stroke_list:
                bgl.glBegin(bgl.GL_LINE_STRIP)
                for point in stroke:
                    x1 = point[0] - viewport[0]
                    y1 = point[1] - viewport[1]
                    bgl.glVertex2i(x1,y1)
                bgl.glEnd()
        
        bgl.glBegin(bgl.GL_LINE_STRIP)
        
        for point in self.point_list:
            x1 = point[0] - viewport[0]
            y1 = point[1] - viewport[1]
            bgl.glVertex2i(x1,y1)
            
        bgl.glEnd()

    def convert_stroke_to_mesh(self):
        verts = []
        edges = []
        
        if len(self.stroke_list_3d) > 0:

            for stroke_index, stroke in enumerate(self.stroke_list_3d):
                voffset = len(self.line_mesh.vertices)
                eoffset = len(self.line_mesh.edges)
                vlen = len(stroke)
                if vlen <= 1:
                    continue
                self.line_mesh.vertices.add(vlen)
                self.line_mesh.edges.add(vlen)
                for index, point in enumerate(stroke):
                    vindex = voffset+index
                    eindex = eoffset+index-1
                    verts.append(vindex)
                    self.line_mesh.vertices[vindex].co = point
                    if index == 0:
                        self.line_mesh.edges[eindex].vertices = [-1, -1]
                    else:
                        eindex = eoffset+index-1
                        edges.append(eindex)
                        self.line_mesh.edges[eindex].vertices = [eindex, eindex+1]

            self.line_mesh.update()
            self.stroke_list_3d = []
            self.stroke_list = []
    
    def draw_lines_3d(self, context, event, viewport):
        
        bgl.glColor4f(0.0, 0.0, 1.0, 1)    
    
        if len(self.stroke_list_3d) > 0:
            for stroke in self.stroke_list_3d:
                bgl.glBegin(bgl.GL_LINE_STRIP)
                for point in stroke:
                    bgl.glVertex3f(point[0],point[1],point[2])
                bgl.glEnd()
    
        bgl.glBegin(bgl.GL_LINE_STRIP)
        
        for point in self.point_list_3d:
            #print(point[0], point[1], point[2])
            bgl.glVertex3f(point[0], point[1], point[2])
            
        bgl.glEnd()
    
        bgl.glColor4f(0.0, 0.0, 0.0, 1)
        
    def draw(self, context, event):
        bgl.glEnable(bgl.GL_BLEND)
        bgl.glColor4f(0.0, 0.0, 0.0, 1)
        bgl.glLineWidth(1.5)
    
        self.init_buffers(context)
        bgl.glGetIntegerv(bgl.GL_VIEWPORT, self.viewport)
            
        # draw!
        self.draw_lines_3d(context, event, self.viewport)
        self.draw_lines(context, event, self.viewport)

        # restore opengl defaults
        bgl.glLineWidth(1)
        bgl.glDisable(bgl.GL_BLEND)
        bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
        

class DrawerRegister(bpy.types.Operator):
    bl_idname = "uimac.drawer"
    bl_label = "uimac Drawer"
    
    drawer = LineDrawer()
    handle = None

    def modal(self, context, event):
        if context.area:
            context.area.tag_redraw()
        
        self.drawer.mouse(context, event)
        
        if event.type in ('ESC'):
            if self.handle:
                self.drawer.dispose()
                context.region.callback_remove(self.handle)
            return {'CANCELLED'}

        return {"PASS_THROUGH"}

    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
            self.cursor_on_handle = 'None'
            context.window_manager.modal_handler_add(self)
            
            #bpy.ops.object.mode_set(mode='OBJECT')
            
            self.handle = context.region.callback_add(\
                self.drawer.draw, (context, event), 'POST_PIXEL')
      
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "Image View not found, cannot run operator")
            return {'CANCELLED'}

def register():
    bpy.utils.register_class(DrawerRegister)
def unregister():
    bpy.utils.unregister_class(DrawerRegister)
    
if __name__ == '__main__':
    register()

0 件のコメント: