Image Empties: Usability improvements and fixes
- 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
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
# <pep8-80 compliant>
|
# <pep8-80 compliant>
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
from mathutils import Euler
|
||||||
from bpy.types import Operator
|
from bpy.types import Operator
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
BoolProperty,
|
BoolProperty,
|
||||||
@@ -28,6 +29,8 @@ from bpy.props import (
|
|||||||
StringProperty,
|
StringProperty,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from math import radians
|
||||||
|
|
||||||
|
|
||||||
class SelectPattern(Operator):
|
class SelectPattern(Operator):
|
||||||
"""Select objects matching a naming pattern"""
|
"""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"""
|
"""Select an image file and create a new image empty with it"""
|
||||||
bl_idname = "object.load_image_as_empty"
|
bl_idname = "object.load_image_as_empty"
|
||||||
bl_label = "Load Image as Empty"
|
bl_label = "Load Image as Empty"
|
||||||
bl_options = {'REGISTER'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
filepath: StringProperty(
|
filepath: StringProperty(
|
||||||
subtype='FILE_PATH'
|
subtype='FILE_PATH'
|
||||||
@@ -883,6 +886,11 @@ class LoadImageAsEmpty(Operator):
|
|||||||
filter_image: BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'})
|
filter_image: BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'})
|
||||||
filter_folder: 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):
|
def invoke(self, context, event):
|
||||||
context.window_manager.fileselect_add(self)
|
context.window_manager.fileselect_add(self)
|
||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
@@ -899,10 +907,48 @@ class LoadImageAsEmpty(Operator):
|
|||||||
|
|
||||||
bpy.ops.object.empty_add(type='IMAGE', location=cursor)
|
bpy.ops.object.empty_add(type='IMAGE', location=cursor)
|
||||||
context.active_object.data = image
|
context.active_object.data = image
|
||||||
|
context.active_object.scale = (5, 5, 5)
|
||||||
|
if self.align_view:
|
||||||
|
bpy.ops.object.align_to_view()
|
||||||
return {'FINISHED'}
|
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 = (
|
classes = (
|
||||||
|
AlignObjectsToView,
|
||||||
ClearAllRestrictRender,
|
ClearAllRestrictRender,
|
||||||
DupliOffsetFromCursor,
|
DupliOffsetFromCursor,
|
||||||
IsolateTypeRender,
|
IsolateTypeRender,
|
||||||
|
|||||||
@@ -190,7 +190,8 @@ bool gizmo_window_project_2d(
|
|||||||
|
|
||||||
float plane[4], co[3];
|
float plane[4], co[3];
|
||||||
plane_from_point_normal_v3(plane, mat[3], mat[2]);
|
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];
|
float imat[4][4];
|
||||||
invert_m4_m4(imat, mat);
|
invert_m4_m4(imat, mat);
|
||||||
mul_m4_v3(imat, co);
|
mul_m4_v3(imat, co);
|
||||||
|
|||||||
@@ -469,6 +469,7 @@ struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(
|
|||||||
struct GPUOffScreen *ofs, char err_out[256]);
|
struct GPUOffScreen *ofs, char err_out[256]);
|
||||||
|
|
||||||
struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]);
|
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_quadview_update(struct ScrArea *sa, struct ARegion *ar, bool do_clip);
|
||||||
void ED_view3d_update_viewmat(
|
void ED_view3d_update_viewmat(
|
||||||
struct Depsgraph *depsgraph, struct Scene *scene, struct View3D *v3d, struct ARegion *ar,
|
struct Depsgraph *depsgraph, struct Scene *scene, struct View3D *v3d, struct ARegion *ar,
|
||||||
|
|||||||
@@ -869,9 +869,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv
|
|||||||
{
|
{
|
||||||
Scene *scene = CTX_data_scene(C);
|
Scene *scene = CTX_data_scene(C);
|
||||||
|
|
||||||
Base *base = NULL;
|
|
||||||
Image *ima = NULL;
|
Image *ima = NULL;
|
||||||
Object *ob = NULL;
|
|
||||||
|
|
||||||
ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM);
|
ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM);
|
||||||
if (!ima) {
|
if (!ima) {
|
||||||
@@ -880,26 +878,24 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv
|
|||||||
/* handled below */
|
/* handled below */
|
||||||
id_us_min((ID *)ima);
|
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 */
|
/* either change empty under cursor or create a new empty */
|
||||||
if (base && base->object->type == OB_EMPTY) {
|
if (ob_cursor && ob_cursor->type == OB_EMPTY) {
|
||||||
ob = base->object;
|
|
||||||
DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE);
|
|
||||||
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
|
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 {
|
else {
|
||||||
/* add new empty */
|
ob = ED_object_add_type(C, OB_EMPTY, NULL, NULL, NULL, false);
|
||||||
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);
|
|
||||||
|
|
||||||
/* add under the mouse */
|
|
||||||
ED_object_location_from_view(C, ob->loc);
|
ED_object_location_from_view(C, ob->loc);
|
||||||
ED_view3d_cursor3d_position(C, event->mval, false, 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);
|
BKE_object_empty_draw_type_set(ob, OB_EMPTY_IMAGE);
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
#include "BKE_object.h"
|
#include "BKE_object.h"
|
||||||
#include "BKE_image.h"
|
#include "BKE_image.h"
|
||||||
|
|
||||||
|
#include "DEG_depsgraph.h"
|
||||||
|
|
||||||
#include "DNA_object_types.h"
|
#include "DNA_object_types.h"
|
||||||
#include "DNA_lamp_types.h"
|
#include "DNA_lamp_types.h"
|
||||||
|
|
||||||
@@ -94,6 +96,7 @@ static void gizmo_empty_image_prop_matrix_set(
|
|||||||
Object *ob = igzgroup->state.ob;
|
Object *ob = igzgroup->state.ob;
|
||||||
|
|
||||||
ob->empty_drawsize = matrix[0][0];
|
ob->empty_drawsize = matrix[0][0];
|
||||||
|
DEG_id_tag_update(ob, DEG_TAG_TRANSFORM);
|
||||||
|
|
||||||
float dims[2];
|
float dims[2];
|
||||||
RNA_float_get_array(gz->ptr, "dimensions", dims);
|
RNA_float_get_array(gz->ptr, "dimensions", dims);
|
||||||
|
|||||||
@@ -1528,6 +1528,13 @@ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
|
|||||||
return basact;
|
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)
|
static void deselect_all_tracks(MovieTracking *tracking)
|
||||||
{
|
{
|
||||||
MovieTrackingObject *object;
|
MovieTrackingObject *object;
|
||||||
|
|||||||
Reference in New Issue
Block a user