Image Empties: Usability improvements and fixes
authorJacques Lucke <mail@jlucke.com>
Tue, 9 Oct 2018 12:36:15 +0000 (14:36 +0200)
committerJacques Lucke <mail@jlucke.com>
Tue, 9 Oct 2018 12:36:15 +0000 (14:36 +0200)
- new "Align to View" option when loading a new image
- automatically align to view when dropping an image into a viewport
- larger default size for image empties
- fix image empty gizmo in orthographic view
- new "Align Objects to View" operator

Reviewer: brecht

Differential: https://developer.blender.org/D3778

release/scripts/startup/bl_operators/object.py
source/blender/editors/gizmo_library/gizmo_library_utils.c
source/blender/editors/include/ED_view3d.h
source/blender/editors/object/object_add.c
source/blender/editors/space_view3d/view3d_gizmo_empty.c
source/blender/editors/space_view3d/view3d_select.c

index f68ebfc4b9432acb94e29ad1a62929379558ffa7..d5175d31cf925804931316e762f0218e7402cdfb 100644 (file)
@@ -19,6 +19,7 @@
 # <pep8-80 compliant>
 
 import bpy
+from mathutils import Euler
 from bpy.types import Operator
 from bpy.props import (
     BoolProperty,
@@ -28,6 +29,8 @@ from bpy.props import (
     StringProperty,
 )
 
+from math import radians
+
 
 class SelectPattern(Operator):
     """Select objects matching a naming pattern"""
@@ -874,7 +877,7 @@ class LoadImageAsEmpty(Operator):
     """Select an image file and create a new image empty with it"""
     bl_idname = "object.load_image_as_empty"
     bl_label = "Load Image as Empty"
-    bl_options = {'REGISTER'}
+    bl_options = {'REGISTER', 'UNDO'}
 
     filepath: StringProperty(
         subtype='FILE_PATH'
@@ -883,6 +886,11 @@ class LoadImageAsEmpty(Operator):
     filter_image: BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'})
     filter_folder: BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'})
 
+    align_view: BoolProperty(
+        name="Align to view",
+        default=True
+    )
+
     def invoke(self, context, event):
         context.window_manager.fileselect_add(self)
         return {'RUNNING_MODAL'}
@@ -899,10 +907,48 @@ class LoadImageAsEmpty(Operator):
 
         bpy.ops.object.empty_add(type='IMAGE', location=cursor)
         context.active_object.data = image
+        context.active_object.scale = (5, 5, 5)
+        if self.align_view:
+            bpy.ops.object.align_to_view()
         return {'FINISHED'}
 
+class AlignObjectsToView(bpy.types.Operator):
+    bl_idname = "object.align_to_view"
+    bl_label = "Align Objects to View"
+    bl_options = {"REGISTER", "UNDO"}
+
+    axis_data = {
+         "X": Euler((0, radians(-90), 0)),
+        "-X": Euler((0, radians(90), 0)),
+         "Y": Euler((radians(90), 0, 0)),
+        "-Y": Euler((radians(-90), 0, 0)),
+         "Z": Euler((0, 0, 0)),
+        "-Z": Euler((0, radians(180), 0))
+    }
+
+    front_axis: EnumProperty(
+        name="Front Axis",
+        default="Z",
+        items=[(name, name, "") for name in axis_data.keys()]
+    )
+
+    @classmethod
+    def poll(cls, context):
+        return context.space_data.type == "VIEW_3D"
+
+    def execute(self, context):
+        base = self.axis_data[self.front_axis].to_matrix()
+
+        view = context.space_data.region_3d.view_matrix
+        rotation = (view.to_3x3().inverted() @ base).to_euler()
+        for object in context.selected_objects:
+            object.rotation_euler = rotation
+
+        return {"FINISHED"}
+
 
 classes = (
+    AlignObjectsToView,
     ClearAllRestrictRender,
     DupliOffsetFromCursor,
     IsolateTypeRender,
index 350c992217953f4923c5cedc6a122943a6693e6d..253646527852e92a978258025284341bdd43adb8 100644 (file)
@@ -190,7 +190,8 @@ bool gizmo_window_project_2d(
 
                float plane[4], co[3];
                plane_from_point_normal_v3(plane, mat[3], mat[2]);
-               if (ED_view3d_win_to_3d_on_plane(ar, plane, mval, true, co)) {
+               bool clip_ray = ((RegionView3D *)ar->regiondata)->is_persp;
+               if (ED_view3d_win_to_3d_on_plane(ar, plane, mval, clip_ray, co)) {
                        float imat[4][4];
                        invert_m4_m4(imat, mat);
                        mul_m4_v3(imat, co);
index 713cc21c8ec8601d7ba4082c46dabd3b4dc0beb2..560ed0cf0e05ea81a81f91460b097ee86eb639a0 100644 (file)
@@ -469,6 +469,7 @@ struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(
         struct GPUOffScreen *ofs, char err_out[256]);
 
 struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]);
+struct Object *ED_view3d_give_object_under_cursor(struct bContext *C, const int mval[2]);
 void ED_view3d_quadview_update(struct ScrArea *sa, struct ARegion *ar, bool do_clip);
 void ED_view3d_update_viewmat(
         struct Depsgraph *depsgraph, struct Scene *scene, struct View3D *v3d, struct ARegion *ar,
index 3fccb5f6943b4063c244ee7af56e6cc2fb71b8f6..4963b304483a120dcc072501db0a73c5b1f7002b 100644 (file)
@@ -869,9 +869,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv
 {
        Scene *scene = CTX_data_scene(C);
 
-       Base *base = NULL;
        Image *ima = NULL;
-       Object *ob = NULL;
 
        ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM);
        if (!ima) {
@@ -880,26 +878,24 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv
        /* handled below */
        id_us_min((ID *)ima);
 
-       base = ED_view3d_give_base_under_cursor(C, event->mval);
+       Object *ob = NULL;
+       Object *ob_cursor = ED_view3d_give_object_under_cursor(C, event->mval);
 
-       /* if empty under cursor, then set object */
-       if (base && base->object->type == OB_EMPTY) {
-               ob = base->object;
-               DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE);
+       /* either change empty under cursor or create a new empty */
+       if (ob_cursor && ob_cursor->type == OB_EMPTY) {
                WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+               DEG_id_tag_update((ID *)ob_cursor, DEG_TAG_TRANSFORM);
+               ob = ob_cursor;
        }
        else {
-               /* add new empty */
-               float rot[3];
-
-               if (!ED_object_add_generic_get_opts(C, op, 'Z', NULL, rot, NULL, NULL))
-                       return OPERATOR_CANCELLED;
-
-               ob = ED_object_add_type(C, OB_EMPTY, NULL, NULL, rot, false);
+               ob = ED_object_add_type(C, OB_EMPTY, NULL, NULL, NULL, false);
 
-               /* add under the mouse */
                ED_object_location_from_view(C, ob->loc);
                ED_view3d_cursor3d_position(C, event->mval, false, ob->loc);
+               ED_object_rotation_from_view(C, ob->rot, 'Z');
+               ob->size[0] = 5;
+               ob->size[1] = 5;
+               ob->size[2] = 5;
        }
 
        BKE_object_empty_draw_type_set(ob, OB_EMPTY_IMAGE);
index 2913ba245e715a5cb434f0c5bad836e896e0910e..8bbc9c8f553e46765b9588f6794ec09e40a0c173 100644 (file)
@@ -31,6 +31,8 @@
 #include "BKE_object.h"
 #include "BKE_image.h"
 
+#include "DEG_depsgraph.h"
+
 #include "DNA_object_types.h"
 #include "DNA_lamp_types.h"
 
@@ -94,6 +96,7 @@ static void gizmo_empty_image_prop_matrix_set(
        Object *ob = igzgroup->state.ob;
 
        ob->empty_drawsize = matrix[0][0];
+       DEG_id_tag_update(ob, DEG_TAG_TRANSFORM);
 
        float dims[2];
        RNA_float_get_array(gz->ptr, "dimensions", dims);
index 26a9a8f24c1023539e4a9dfef5bb9c4a79747f28..47266c2584e280529b00e4ec9ebd1e7ce469756c 100644 (file)
@@ -1528,6 +1528,13 @@ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
        return basact;
 }
 
+Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2])
+{
+       Base *base = ED_view3d_give_base_under_cursor(C, mval);
+       if (base) return base->object;
+       return NULL;
+}
+
 static void deselect_all_tracks(MovieTracking *tracking)
 {
        MovieTrackingObject *object;