4
$\begingroup$

I want to place an object where the cursor is using python. So I need a script that place the 3D cursor where I have the cursor at that moment and then place the already selected object there. To do that I created a quick favorite that call that script loaded as addon (image below) enter image description here

But I can't get the 3D cursor where the "windows cursor" is pointing. I'd need a script that emulate what the shift+RMB does

I'm just a newbie, please have mercy :) since my code may be neither elegant nor good

bl_info = {
    "name": "Place object",
    "blender": (4, 0 ,2),
    "category": "Object",
}


import bpy
import mathutils


class OBJECT_OT_placeObject(bpy.types.Operator):
    """Place object where the cursor is"""
    bl_idname = "object.place"
    bl_label = "Place Object"
    bl_options = {"REGISTER", "UNDO"}

    def execute(self, context): 
       
        # Mouse cursor coordinates
        mouse_x = bpy.context.region.width // 2
        mouse_y = bpy.context.region.height // 2

        # Get the depsgraph actual
        depsgraph = bpy.context.evaluated_depsgraph_get()

        # Convert mouse coordinates to a ray in 3D space
        ray = bpy.context.scene.ray_cast(depsgraph, origin=bpy.context.area.spaces[0].region_3d.view_location, direction=mathutils.Vector((mouse_x, mouse_y, 0.0)))

        # test if ray intersetcs
        if ray[0]:
            # Mover  3D where the mouse cursor is pointing
            bpy.context.scene.cursor.location = ray[1]
        else:
            print("No se encontró intersección")


        # -----------------------------------------------------------
        # Place object where the cursor is


        # Get the 3D cursor position
        cursor_location = bpy.context.scene.cursor.location

        # Get the selected object
        selected_object = bpy.context.selected_objects[0]

        # Move selected object where the 3D cursor is
        selected_object.location = cursor_location


def menu_func(self, context):
    self.layout.operator(OBJECT_OT_placeObject.bl_idname)

def register():
    bpy.utils.register_class(OBJECT_OT_placeObject)
    bpy.types.VIEW3D_MT_object.append(menu_func)

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

if __name__ == "__main__":
    register()
```
$\endgroup$

2 Answers 2

4
$\begingroup$

If you have a look at the user preferences, you can see the operator that is triggered by Shift + RMB :

enter image description here

That's bpy.ops.view3d.cursor3d() you can find it in the docs, which apparently is really not that useful in this case if you ask me. It's tricky to use from an operator and I have no idea how one is supposed to know that it needs to be run with 'INVOKE_DEFAULT', but it does. So it should be bpy.ops.view3d.cursor3d('INVOKE_DEFAULT') and it can set 3d cursor location to the mouse position from another operator.

So that would be:

import bpy


class OBJECT_OT_placeObject(bpy.types.Operator):
    """Place object where the cursor is"""
    bl_idname = "object.place"
    bl_label = "Place Object"

    @classmethod
    def poll(cls, context):
        return context.object is not None

    def execute(self, context):
        previous_cursor_location = context.scene.cursor.location.copy()
        bpy.ops.view3d.cursor3d('INVOKE_DEFAULT') 
        context.object.location = context.scene.cursor.location
        context.scene.cursor.location = previous_cursor_location
        return {'FINISHED'}


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

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


if __name__ == "__main__":
    register()
$\endgroup$
3
  • $\begingroup$ It works like a charm... so simple so effective. Less is more. I have much to learn yet. Thank you very much $\endgroup$
    – Víctor GV
    Commented May 14 at 4:38
  • 1
    $\begingroup$ AFIK, we should avoid the use of bpy.ops inside custom scripts. $\endgroup$
    – Karan
    Commented May 14 at 5:21
  • 1
    $\begingroup$ @VíctorGV, Karan is right though, keep in mind it might not always be the best idea to go too crazy with operators in your code. I think this one is probably fine though and I think it makes some sense in this particular case, but it's not "the best" way, just one way to do it. $\endgroup$ Commented May 14 at 6:38
4
$\begingroup$

You should use a modal operator

import bpy
from bpy.types import Operator
from bpy_extras.view3d_utils import region_2d_to_location_3d
from mathutils import Vector


class OBJECT_OT_place_object(Operator):
    """Place object to the 3D cursor"""

    bl_idname = "object.place"
    bl_label = "Place Object"
    bl_options = {"REGISTER", "UNDO"}

    def invoke(self, context, event):
        self.cursor_location = context.scene.cursor.location.copy()
        context.window_manager.modal_handler_add(self)
        return {"RUNNING_MODAL"}

    def modal(self, context, event):
        cursor_location = region_2d_to_location_3d(
            context.region,
            context.space_data.region_3d,
            (event.mouse_region_x, event.mouse_region_y),
            Vector((0, 0, 0)),
        )
        context.scene.cursor.location = cursor_location

        if event.type in {"RIGHTMOUSE", "ESC"}:
            context.scene.cursor.location = self.cursor_location
            return {"CANCELLED"}

        elif event.type == "LEFTMOUSE":
            for obj in context.selected_objects:
                obj.location = cursor_location
            return {"FINISHED"}

        return {"PASS_THROUGH"}


def menu_func(self, context):
    self.layout.operator(OBJECT_OT_place_object.bl_idname)


def register():
    bpy.utils.register_class(OBJECT_OT_place_object)
    bpy.types.VIEW3D_MT_object.append(menu_func)


def unregister():
    bpy.utils.unregister_class(OBJECT_OT_place_object)
    bpy.types.VIEW3D_MT_object.remove(menu_func)


if __name__ == "__main__":
    register()
$\endgroup$
2
  • 2
    $\begingroup$ Hard to reed those long lines. :D I finally start to get what this whole PEP8 thing is about. :D $\endgroup$ Commented May 13 at 23:06
  • $\begingroup$ It might be nice to avoid using operators in code since they are really meant for the user, but in this case... I don't know... modal operator seems to be a bit of a hassle to me. Still a perfectly good answer. $\endgroup$ Commented May 13 at 23:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .