1
$\begingroup$

I am looking for a code, that adds an uv sphere on a mesh when a mouse click happens. I saw the topic: Adding new object on mouse click? but there are no answers there. I guess a modal should be implemented on the "LEFTMOUSE" clicks, but I can't seem to find any simple examples and explanations.

import bpy
import bgl
import bmesh
from bpy_extras import view3d_utils
import mathutils
from mathutils import Vector

"""Functions for the mouse_coords_to_3D_view"""
def get_viewport():
    view = bgl.Buffer(bgl.GL_INT, 4)
    bgl.glGetIntegerv(bgl.GL_VIEWPORT, view)
    return view


def get_modelview_matrix():
    model_matrix = bgl.Buffer(bgl.GL_DOUBLE, [4, 4])
    bgl.glGetDoublev(bgl.GL_MODELVIEW_MATRIX, model_matrix)
    return model_matrix


def get_projection_matrix():
    proj_matrix = bgl.Buffer(bgl.GL_DOUBLE, [4, 4])
    bgl.glGetDoublev(bgl.GL_PROJECTION_MATRIX, proj_matrix)
    return proj_matrix


"""Function mouse_coords_to_3D_view"""
def mouse_coords_to_3D_view(x, y):
    depth = bgl.Buffer(bgl.GL_FLOAT, [0.0])
    bgl.glReadPixels(x, y, 1, 1, bgl.GL_DEPTH_COMPONENT, bgl.GL_FLOAT, depth)
    world_x = bgl.Buffer(bgl.GL_DOUBLE, 1, [0.0])
    world_y = bgl.Buffer(bgl.GL_DOUBLE, 1, [0.0])
    world_z = bgl.Buffer(bgl.GL_DOUBLE, 1, [0.0])
    view1 = get_viewport()
    model = get_modelview_matrix()
    proj = get_projection_matrix ()
    bgl.gluUnProject(x, y, depth[0],
                     model, proj,
                     view1,
                     world_x, world_y, world_z)
    return float(world_x[0]), float(world_y[0]), float(world_z[0])

"""drawing point OpenGL in mouse_coords_to_3D_view"""
def draw_callback_px(self, context):
    # mouse coordinates relative to 3d view
    x, y = self.mouse_path
    mx, my = self.mx, self.my #got from modal

    # mouse coordinates relative to Blender interface
    view = get_viewport()
    gmx = view[0] + x
    gmy = view[1] + y

    if False:
        #c= bgl.Buffer(bgl.GL_UNSIGNED_BYTE, [3,1])
        #bgl.glReadPixels(x, y, 1, 1, bgl.GL_RGB, bgl.GL_FLOAT, c)
        #c= bgl.Buffer(bgl.GL_SHORT, [3,1])
        c = bgl.Buffer(bgl.GL_FLOAT, [3,1])
        bgl.glReadPixels(gmx, gmy,1,1,bgl.GL_RGB,bgl.GL_FLOAT,c);
        draw_square_follow_cursor(c, gmx, gmy)
    else:
        draw_uv_sphere(mx, my, 0.5)


def draw_uv_sphere(mx, my, s):
    ob = bpy.data.objects['Cube']
    v = ob.data.vertices[0].co
    mat = ob.matrix_world
    x,y,z = mouse_coords_to_3D_view(mx, my)
    mv = Vector((x, y, z))
    bpy.ops.mesh.primitive_uv_sphere_add(segments=42, ring_count=42, location=mat * mv, size=s)


def draw_square_follow_cursor(c, gmx, gmy):

    mouse3d = mouse_coords_to_3D_view(gmx, gmy)
    bgl.glEnable(bgl.GL_BLEND)
    bgl.glColor3f(0.0,0.0,255.0)
    bgl.glPointSize(30)
    bgl.glBegin(bgl.GL_POINTS)
    bgl.glVertex3f(*(mouse3d))
    bgl.glVertex3f(mouse3d[0], mouse3d[1], mouse3d[2])
    bgl.glVertex2f(gmx,gmy)
    bgl.glEnd()

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

def draw_corner_square(c):
    for area in bpy.context.screen.areas:
        if area.type=='VIEW_3D':
            X= area.x
            Y= area.y
    dist= 100 #distancia del punto al cursor
    mouse3d = mouse_coords_to_3D_view(X+dist,Y+dist)
    bgl.glEnable(bgl.GL_BLEND)
    bgl.glColor3f(c[0][0],c[1][0],c[2][0])
    bgl.glPointSize(30)
    bgl.glBegin(bgl.GL_POINTS)
    bgl.glVertex3f(mouse3d[0], mouse3d[1], mouse3d[2])
    bgl.glEnd()



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


def draw_square(c):
    bgl.glEnable(bgl.GL_BLEND)
    bgl.glColor3f(c[0][0],c[1][0],c[2][0])
    bgl.glBegin(bgl.GL_POLYGON)
    bgl.glVertex2f(-0.5, -0.5)
    bgl.glVertex2f(-0.5, 0.5)
    bgl.glVertex2f(0.5, 0.5)
    bgl.glVertex2f(0.5, -0.5)
    bgl.glEnd( )

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

class ModalDrawOperator(bpy.types.Operator):
    """Draw a point with the mouse"""
    bl_idname = "view3d.modal_operator"
    bl_label = "Simple Modal View3D Operator"


    def execute(self, context):
        draw_uv_sphere(self.mx, self.my, 0.5)
        return {'PASS_THROUGH'}

    def modal(self, context, event):
        #context.area.tag_redraw()

        if event.type == 'MOUSEMOVE':
            self.mouse_path = (event.mouse_region_x, event.mouse_region_y)
        elif event.type == 'LEFTMOUSE':
            self.mx, self.my= event.mouse_x, event.mouse_y
            self.execute(context)
            return {'PASS_THROUGH'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            if False:
                bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
                context.area.header_text_set()
            return {'CANCELLED'}


        return {'PASS_THROUGH'}


    def invoke(self, context, event):
        # the arguments we pass the the callback
        args = (self, context)
        # Add the region OpenGL drawing callback
        # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
        #self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_VIEW')
        self.mouse_path = []
        self.width = bpy.context.window.width
        self.height = bpy.context.window.height
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}


def register():
    bpy.utils.register_class(ModalDrawOperator)


def unregister():
    bpy.utils.unregister_class(ModalDrawOperator)


if __name__ == "__main__":
    register()

Thanks.

$\endgroup$
2
$\begingroup$

Similarly to This Answer The following adds a sphere on left mouse press. Needs to be invoked from a 3d view.

import bpy
from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_location_3d

class ModalTimerOperator(bpy.types.Operator):
    """Operator which runs its self from a timer"""
    bl_idname = "wm.modal_timer_operator"
    bl_label = "Add Sphere on Click"

    _timer = None

    def modal(self, context, event):
        if event.type in {'RIGHTMOUSE', 'ESC'}:
            self.cancel(context)
            return {'CANCELLED'}

        if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
            # left click

            if context.area.type == 'VIEW_3D':
                region = context.region
                r3d = context.space_data.region_3d
                x, y = event.mouse_region_x, event.mouse_region_y
                view = region_2d_to_vector_3d(region, r3d, (x, y))
                loc = region_2d_to_location_3d(region, r3d, (x, y), view)
                bpy.ops.mesh.primitive_uv_sphere_add(location=loc)

        if event.type == 'TIMER':
            #print("timer")
            pass


        return {'PASS_THROUGH'}

    def execute(self, context):
        if context.area.type != 'VIEW_3D':
            print("Must use in a 3d region")
            return {'CANCELLED'}

        wm = context.window_manager
        self._timer = wm.event_timer_add(0.1, context.window)
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        wm = context.window_manager
        wm.event_timer_remove(self._timer)


def register():
    bpy.utils.register_class(ModalTimerOperator)


def unregister():
    bpy.utils.unregister_class(ModalTimerOperator)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.wm.modal_timer_operator()

# note code edited from text editor > templates > python > modal timer operator.
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.