Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / object / object_transform.c
index 19dad37..cb609b2 100644 (file)
 #include "BLI_math.h"
 #include "BLI_listbase.h"
 #include "BLI_utildefines.h"
+#include "BLI_array.h"
 
 #include "BKE_context.h"
 #include "BKE_curve.h"
-#include "BKE_depsgraph.h"
 #include "BKE_main.h"
 #include "BKE_idcode.h"
 #include "BKE_mball.h"
@@ -60,6 +60,8 @@
 #include "BKE_lattice.h"
 #include "BKE_tracking.h"
 
+#include "DEG_depsgraph.h"
+
 #include "RNA_define.h"
 #include "RNA_access.h"
 
@@ -72,6 +74,8 @@
 #include "ED_screen.h"
 #include "ED_view3d.h"
 
+#include "MEM_guardedalloc.h"
+
 #include "object_intern.h"
 
 /*************************** Clear Transformation ****************************/
@@ -266,7 +270,7 @@ static int object_clear_transform_generic_exec(bContext *C, wmOperator *op,
                        ED_autokeyframe_object(C, scene, ob, ks);
 
                        /* tag for updates */
-                       DAG_id_tag_update(&ob->id, OB_RECALC_OB);
+                       DEG_id_tag_update(&ob->id, OB_RECALC_OB);
                }
        }
        CTX_DATA_END;
@@ -372,7 +376,7 @@ static int object_origin_clear_exec(bContext *C, wmOperator *UNUSED(op))
                        mul_m3_v3(mat, v3);
                }
 
-               DAG_id_tag_update(&ob->id, OB_RECALC_OB);
+               DEG_id_tag_update(&ob->id, OB_RECALC_OB);
        }
        CTX_DATA_END;
 
@@ -400,16 +404,17 @@ void OBJECT_OT_origin_clear(wmOperatorType *ot)
 
 /* use this when the loc/size/rot of the parent has changed but the children
  * should stay in the same place, e.g. for apply-size-rot or object center */
-static void ignore_parent_tx(Main *bmain, Scene *scene, Object *ob)
+static void ignore_parent_tx(const bContext *C, Main *bmain, Scene *scene, Object *ob)
 {
        Object workob;
        Object *ob_child;
+       Depsgraph *depsgraph = CTX_data_depsgraph(C);
 
        /* a change was made, adjust the children to compensate */
        for (ob_child = bmain->object.first; ob_child; ob_child = ob_child->id.next) {
                if (ob_child->parent == ob) {
                        BKE_object_apply_mat4(ob_child, ob_child->obmat, true, false);
-                       BKE_object_workob_calc_parent(scene, ob_child, &workob);
+                       BKE_object_workob_calc_parent(depsgraph, scene, ob_child, &workob);
                        invert_m4_m4(ob_child->parentinv, workob.obmat);
                }
        }
@@ -422,6 +427,7 @@ static int apply_objects_internal(
 {
        Main *bmain = CTX_data_main(C);
        Scene *scene = CTX_data_scene(C);
+       Depsgraph *depsgraph = CTX_data_depsgraph(C);
        float rsmat[3][3], obmat[3][3], iobmat[3][3], mat[4][4], scale;
        bool changed = true;
 
@@ -538,7 +544,7 @@ static int apply_objects_internal(
                        Mesh *me = ob->data;
 
                        if (apply_scale)
-                               multiresModifier_scale_disp(scene, ob);
+                               multiresModifier_scale_disp(depsgraph, scene, ob);
 
                        /* adjust data */
                        BKE_mesh_transform(me, mat, true);
@@ -625,6 +631,10 @@ static int apply_objects_internal(
                                la->area_shape = LA_AREA_RECT;
                                la->area_sizey = la->area_size;
                        }
+                       else if ((la->area_shape == LA_AREA_DISK) && !keeps_aspect_ratio) {
+                               la->area_shape = LA_AREA_ELLIPSE;
+                               la->area_sizey = la->area_size;
+                       }
 
                        la->area_size *= rsmat[0][0];
                        la->area_sizey *= rsmat[1][1];
@@ -644,14 +654,14 @@ static int apply_objects_internal(
                        unit_axis_angle(ob->rotAxis, &ob->rotAngle);
                }
 
-               BKE_object_where_is_calc(scene, ob);
+               BKE_object_where_is_calc(depsgraph, scene, ob);
                if (ob->type == OB_ARMATURE) {
-                       BKE_pose_where_is(scene, ob); /* needed for bone parents */
+                       BKE_pose_where_is(depsgraph, scene, ob); /* needed for bone parents */
                }
 
-               ignore_parent_tx(bmain, scene, ob);
+               ignore_parent_tx(C, bmain, scene, ob);
 
-               DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA);
+               DEG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA);
 
                changed = true;
        }
@@ -669,16 +679,17 @@ static int apply_objects_internal(
 static int visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op))
 {
        Scene *scene = CTX_data_scene(C);
+       Depsgraph *depsgraph = CTX_data_depsgraph(C);
        bool changed = false;
 
        CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects)
        {
-               BKE_object_where_is_calc(scene, ob);
+               BKE_object_where_is_calc(depsgraph, scene, ob);
                BKE_object_apply_mat4(ob, ob->obmat, true, true);
-               BKE_object_where_is_calc(scene, ob);
+               BKE_object_where_is_calc(depsgraph, scene, ob);
 
                /* update for any children that may get moved */
-               DAG_id_tag_update(&ob->id, OB_RECALC_OB);
+               DEG_id_tag_update(&ob->id, OB_RECALC_OB);
 
                changed = true;
        }
@@ -759,6 +770,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
        Scene *scene = CTX_data_scene(C);
        Object *obact = CTX_data_active_object(C);
        Object *obedit = CTX_data_edit_object(C);
+       Depsgraph *depsgraph = CTX_data_depsgraph(C);
        Object *tob;
        float cursor[3], cent[3], cent_neg[3], centn[3];
        int centermode = RNA_enum_get(op->ptr, "type");
@@ -778,9 +790,9 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
        else {
                /* get the view settings if 'around' isn't set and the view is available */
                View3D *v3d = CTX_wm_view3d(C);
-               copy_v3_v3(cursor, ED_view3d_cursor3d_get(scene, v3d));
+               copy_v3_v3(cursor, ED_view3d_cursor3d_get(scene, v3d)->location);
                if (v3d && !RNA_struct_property_is_set(op->ptr, "center"))
-                       around = v3d->around;
+                       around = scene->toolsettings->transform_pivot_point;
        }
 
        zero_v3(cent);
@@ -822,7 +834,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
 
                        EDBM_mesh_normals_update(em);
                        tot_change++;
-                       DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+                       DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
                }
        }
 
@@ -871,7 +883,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
 
                        if (ob->data == NULL) {
                                /* special support for dupligroups */
-                               if ((ob->transflag & OB_DUPLIGROUP) && ob->dup_group && (ob->dup_group->id.tag & LIB_TAG_DOIT) == 0) {
+                               if ((ob->transflag & OB_DUPLICOLLECTION) && ob->dup_group && (ob->dup_group->id.tag & LIB_TAG_DOIT) == 0) {
                                        if (ID_IS_LINKED(ob->dup_group)) {
                                                tot_lib_error++;
                                        }
@@ -883,7 +895,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
                                                        float min[3], max[3];
                                                        /* only bounds support */
                                                        INIT_MINMAX(min, max);
-                                                       BKE_object_minmax_dupli(bmain, scene, ob, min, max, true);
+                                                       BKE_object_minmax_dupli(depsgraph, scene, ob, min, max, true);
                                                        mid_v3_v3v3(cent, min, max);
                                                        invert_m4_m4(ob->imat, ob->obmat);
                                                        mul_m4_v3(ob->imat, cent);
@@ -947,7 +959,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
 
                                if (obedit) {
                                        if (centermode == GEOMETRY_TO_ORIGIN) {
-                                               DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+                                               DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
                                        }
                                        break;
                                }
@@ -994,16 +1006,16 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
                                        /* Function to recenter armatures in editarmature.c
                                         * Bone + object locations are handled there.
                                         */
-                                       ED_armature_origin_set(scene, ob, cursor, centermode, around);
+                                       ED_armature_origin_set(ob, cursor, centermode, around);
 
                                        tot_change++;
                                        arm->id.tag |= LIB_TAG_DOIT;
                                        /* do_inverse_offset = true; */ /* docenter_armature() handles this */
 
-                                       BKE_object_where_is_calc(scene, ob);
-                                       BKE_pose_where_is(scene, ob); /* needed for bone parents */
+                                       BKE_object_where_is_calc(depsgraph, scene, ob);
+                                       BKE_pose_where_is(depsgraph, scene, ob); /* needed for bone parents */
 
-                                       ignore_parent_tx(bmain, scene, ob);
+                                       ignore_parent_tx(C, bmain, scene, ob);
 
                                        if (obedit)
                                                break;
@@ -1025,7 +1037,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
 
                                if (obedit) {
                                        if (centermode == GEOMETRY_TO_ORIGIN) {
-                                               DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+                                               DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
                                        }
                                        break;
                                }
@@ -1059,12 +1071,12 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
 
                                add_v3_v3(ob->loc, centn);
 
-                               BKE_object_where_is_calc(scene, ob);
+                               BKE_object_where_is_calc(depsgraph, scene, ob);
                                if (ob->type == OB_ARMATURE) {
-                                       BKE_pose_where_is(scene, ob); /* needed for bone parents */
+                                       BKE_pose_where_is(depsgraph, scene, ob); /* needed for bone parents */
                                }
 
-                               ignore_parent_tx(bmain, scene, ob);
+                               ignore_parent_tx(C, bmain, scene, ob);
 
                                /* other users? */
                                //CTX_DATA_BEGIN (C, Object *, ob_other, selected_editable_objects)
@@ -1080,19 +1092,19 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
                                        if ((ob_other->flag & OB_DONE) == 0 &&
                                            ((ob->data && (ob->data == ob_other->data)) ||
                                             (ob->dup_group == ob_other->dup_group &&
-                                             (ob->transflag | ob_other->transflag) & OB_DUPLIGROUP)))
+                                             (ob->transflag | ob_other->transflag) & OB_DUPLICOLLECTION)))
                                        {
                                                ob_other->flag |= OB_DONE;
-                                               DAG_id_tag_update(&ob_other->id, OB_RECALC_OB | OB_RECALC_DATA);
+                                               DEG_id_tag_update(&ob_other->id, OB_RECALC_OB | OB_RECALC_DATA);
 
                                                mul_v3_mat3_m4v3(centn, ob_other->obmat, cent); /* omit translation part */
                                                add_v3_v3(ob_other->loc, centn);
 
-                                               BKE_object_where_is_calc(scene, ob_other);
+                                               BKE_object_where_is_calc(depsgraph, scene, ob_other);
                                                if (ob_other->type == OB_ARMATURE) {
-                                                       BKE_pose_where_is(scene, ob_other); /* needed for bone parents */
+                                                       BKE_pose_where_is(depsgraph, scene, ob_other); /* needed for bone parents */
                                                }
-                                               ignore_parent_tx(bmain, scene, ob_other);
+                                               ignore_parent_tx(C, bmain, scene, ob_other);
                                        }
                                }
                                //CTX_DATA_END;
@@ -1101,9 +1113,12 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
        }
        BLI_freelistN(&ctx_data_list);
 
-       for (tob = bmain->object.first; tob; tob = tob->id.next)
-               if (tob->data && (((ID *)tob->data)->tag & LIB_TAG_DOIT))
-                       DAG_id_tag_update(&tob->id, OB_RECALC_OB | OB_RECALC_DATA);
+       for (tob = bmain->object.first; tob; tob = tob->id.next) {
+               if (tob->data && (((ID *)tob->data)->tag & LIB_TAG_DOIT)) {
+                       BKE_mesh_batch_cache_dirty(tob->data, BKE_MESH_BATCH_DIRTY_ALL);
+                       DEG_id_tag_update(&tob->id, OB_RECALC_OB | OB_RECALC_DATA);
+               }
+       }
 
        if (tot_change) {
                WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
@@ -1160,3 +1175,385 @@ void OBJECT_OT_origin_set(wmOperatorType *ot)
        ot->prop = RNA_def_enum(ot->srna, "type", prop_set_center_types, 0, "Type", "");
        RNA_def_enum(ot->srna, "center", prop_set_bounds_types, V3D_AROUND_CENTER_MEAN, "Center", "");
 }
+
+/* -------------------------------------------------------------------- */
+
+/** \name Transform Axis Target
+ *
+ * Note this is an experemental operator to point lamps/cameras at objects.
+ * We may re-work how this behaves based on user feedback.
+ * - campbell.
+ * \{ */
+
+/* When using multiple objects, apply their relative rotational offset to the active object. */
+#define USE_RELATIVE_ROTATION
+
+struct XFormAxisItem {
+       Object *ob;
+       float rot_mat[3][3];
+       void *obtfm;
+       float xform_dist;
+
+#ifdef USE_RELATIVE_ROTATION
+       /* use when translating multiple */
+       float xform_rot_offset[3][3];
+#endif
+};
+
+struct XFormAxisData {
+       ViewContext vc;
+       struct {
+               float depth;
+               float normal[3];
+               bool is_depth_valid;
+               bool is_normal_valid;
+       } prev;
+
+       struct XFormAxisItem *object_data;
+       uint object_data_len;
+       bool is_translate;
+
+       int init_event;
+};
+
+static bool object_is_target_compat(const Object *ob)
+{
+       if (ob->type == OB_LAMP) {
+               const Lamp *la = ob->data;
+               if (ELEM(la->type, LA_SUN, LA_SPOT, LA_HEMI, LA_AREA)) {
+                       return true;
+               }
+       }
+       /* We might want to enable this later, for now just lamps */
+#if 0
+       else if (ob->type == OB_CAMERA) {
+               return true;
+       }
+#endif
+       return false;
+}
+
+static void object_transform_axis_target_free_data(wmOperator *op)
+{
+       struct XFormAxisData *xfd = op->customdata;
+       struct XFormAxisItem *item = xfd->object_data;
+       for (int i = 0; i < xfd->object_data_len; i++, item++) {
+               MEM_freeN(item->obtfm);
+       }
+       MEM_freeN(xfd->object_data);
+       MEM_freeN(xfd);
+       op->customdata = NULL;
+}
+
+/* We may want to expose as alternative to: BKE_object_apply_rotation */
+static void object_apply_rotation(Object *ob, const float rmat[3][3])
+{
+       float size[3];
+       float loc[3];
+       float rmat4[4][4];
+       copy_m4_m3(rmat4, rmat);
+
+       copy_v3_v3(size, ob->size);
+       copy_v3_v3(loc, ob->loc);
+       BKE_object_apply_mat4(ob, rmat4, true, true);
+       copy_v3_v3(ob->size, size);
+       copy_v3_v3(ob->loc, loc);
+}
+/* We may want to extract this to: BKE_object_apply_location */
+static void object_apply_location(Object *ob, const float loc[3])
+{
+       /* quick but weak */
+       Object ob_prev = *ob;
+       float mat[4][4];
+       copy_m4_m4(mat, ob->obmat);
+       copy_v3_v3(mat[3], loc);
+       BKE_object_apply_mat4(ob, mat, true, true);
+       copy_v3_v3(mat[3], ob->loc);
+       *ob = ob_prev;
+       copy_v3_v3(ob->loc, mat[3]);
+}
+
+static void object_orient_to_location(
+        Object *ob, float rot_orig[3][3], const float axis[3], const float location[3])
+{
+       float delta[3];
+       sub_v3_v3v3(delta, ob->obmat[3], location);
+       if (normalize_v3(delta) != 0.0f) {
+               if (len_squared_v3v3(delta, axis) > FLT_EPSILON) {
+                       float delta_rot[3][3];
+                       float final_rot[3][3];
+                       rotation_between_vecs_to_mat3(delta_rot, axis, delta);
+
+                       mul_m3_m3m3(final_rot, delta_rot, rot_orig);
+
+                       object_apply_rotation(ob, final_rot);
+
+                       DEG_id_tag_update(&ob->id, OB_RECALC_OB);
+               }
+       }
+}
+
+static void object_transform_axis_target_cancel(bContext *C, wmOperator *op)
+{
+       struct XFormAxisData *xfd = op->customdata;
+       struct XFormAxisItem *item = xfd->object_data;
+       for (int i = 0; i < xfd->object_data_len; i++, item++) {
+               BKE_object_tfm_restore(item->ob, item->obtfm);
+               DEG_id_tag_update(&item->ob->id, OB_RECALC_OB);
+               WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob);
+       }
+
+       object_transform_axis_target_free_data(op);
+}
+
+static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+       ViewContext vc;
+       ED_view3d_viewcontext_init(C, &vc);
+
+       if (!object_is_target_compat(vc.obact)) {
+               /* Falls back to texture space transform. */
+               return OPERATOR_PASS_THROUGH;
+       }
+
+       ED_view3d_autodist_init(vc.depsgraph, vc.ar, vc.v3d, 0);
+
+       if (vc.rv3d->depths != NULL) {
+               vc.rv3d->depths->damaged = true;
+       }
+       ED_view3d_depth_update(vc.ar);
+
+       if (vc.rv3d->depths == NULL) {
+               BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
+               return OPERATOR_CANCELLED;
+       }
+
+       ED_region_tag_redraw(vc.ar);
+
+       struct XFormAxisData *xfd;
+       xfd = op->customdata = MEM_callocN(sizeof(struct XFormAxisData), __func__);
+
+       /* Don't change this at runtime. */
+       xfd->vc = vc;
+       xfd->vc.mval[0] = event->mval[0];
+       xfd->vc.mval[1] = event->mval[1];
+
+       xfd->prev.depth = 1.0f;
+       xfd->prev.is_depth_valid = false;
+       xfd->prev.is_normal_valid = false;
+       xfd->is_translate = false;
+
+       xfd->init_event = WM_userdef_event_type_from_keymap_type(event->type);
+
+       {
+               struct XFormAxisItem *object_data = NULL;
+               BLI_array_declare(object_data);
+
+               struct XFormAxisItem *item = BLI_array_append_ret(object_data);
+               item->ob = xfd->vc.obact;
+
+               CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects)
+               {
+                       if ((ob != xfd->vc.obact) && object_is_target_compat(ob)) {
+                               item = BLI_array_append_ret(object_data);
+                               item->ob = ob;
+                       }
+               }
+               CTX_DATA_END;
+
+               xfd->object_data = object_data;
+               xfd->object_data_len = BLI_array_len(object_data);
+
+               if (xfd->object_data_len != BLI_array_len(object_data)) {
+                       xfd->object_data = MEM_reallocN(xfd->object_data, xfd->object_data_len * sizeof(*xfd->object_data));
+               }
+       }
+
+       {
+               struct XFormAxisItem *item = xfd->object_data;
+               for (int i = 0; i < xfd->object_data_len; i++, item++) {
+                       item->obtfm = BKE_object_tfm_backup(item->ob);
+                       BKE_object_rot_to_mat3(item->ob, item->rot_mat, true);
+               }
+       }
+
+       WM_event_add_modal_handler(C, op);
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+       struct XFormAxisData *xfd = op->customdata;
+       ARegion *ar = xfd->vc.ar;
+
+       view3d_operator_needs_opengl(C);
+
+       const bool is_translate = (event->ctrl != 0);
+       const bool is_translate_init = is_translate && (xfd->is_translate != is_translate);
+
+       if (event->type == MOUSEMOVE || is_translate_init) {
+               const ViewDepths *depths = xfd->vc.rv3d->depths;
+               if (depths &&
+                   ((unsigned int)event->mval[0] < depths->w) &&
+                   ((unsigned int)event->mval[1] < depths->h))
+               {
+                       double depth = (double)ED_view3d_depth_read_cached(&xfd->vc, event->mval);
+                       float location_world[3];
+                       if (depth == 1.0f) {
+                               if (xfd->prev.is_depth_valid) {
+                                       depth = (double)xfd->prev.depth;
+                               }
+                       }
+                       if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
+                               xfd->prev.depth = depth;
+                               xfd->prev.is_depth_valid = true;
+                               if (ED_view3d_depth_unproject(ar, event->mval, depth, location_world)) {
+                                       if (is_translate) {
+
+                                               float normal[3];
+                                               bool normal_found = false;
+                                               if (ED_view3d_depth_read_cached_normal(&xfd->vc, event->mval, normal)) {
+                                                       normal_found = true;
+
+                                                       /* cheap attempt to smooth normals out a bit! */
+                                                       const uint ofs = 2;
+                                                       for (uint x = -ofs; x <= ofs; x += ofs / 2) {
+                                                               for (uint y = -ofs; y <= ofs; y += ofs / 2) {
+                                                                       if (x != 0 && y != 0) {
+                                                                               int mval_ofs[2] = {event->mval[0] + x, event->mval[1] + y};
+                                                                               float n[3];
+                                                                               if (ED_view3d_depth_read_cached_normal(
+                                                                                       &xfd->vc, mval_ofs, n))
+                                                                               {
+                                                                                       add_v3_v3(normal, n);
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                                       normalize_v3(normal);
+                                               }
+                                               else if (xfd->prev.is_normal_valid) {
+                                                       copy_v3_v3(normal, xfd->prev.normal);
+                                                       normal_found = true;
+                                               }
+
+                                               if (normal_found) {
+#ifdef USE_RELATIVE_ROTATION
+                                                       if (is_translate_init && xfd->object_data_len > 1) {
+                                                               float xform_rot_offset_inv_first[3][3];
+                                                               struct XFormAxisItem *item = xfd->object_data;
+                                                               for (int i = 0; i < xfd->object_data_len; i++, item++) {
+                                                                       copy_m3_m4(item->xform_rot_offset, item->ob->obmat);
+                                                                       normalize_m3(item->xform_rot_offset);
+
+                                                                       if (i == 0) {
+                                                                               invert_m3_m3(xform_rot_offset_inv_first, xfd->object_data[0].xform_rot_offset);
+                                                                       }
+                                                                       else {
+                                                                               mul_m3_m3m3(item->xform_rot_offset,
+                                                                                           item->xform_rot_offset,
+                                                                                           xform_rot_offset_inv_first);
+                                                                       }
+                                                               }
+                                                       }
+
+#endif
+
+                                                       struct XFormAxisItem *item = xfd->object_data;
+                                                       for (int i = 0; i < xfd->object_data_len; i++, item++) {
+                                                               if (is_translate_init) {
+                                                                       float ob_axis[3];
+                                                                       item->xform_dist = len_v3v3(item->ob->obmat[3], location_world);
+                                                                       normalize_v3_v3(ob_axis, item->ob->obmat[2]);
+                                                                       /* Scale to avoid adding distance when moving between surfaces. */
+                                                                       float scale = fabsf(dot_v3v3(ob_axis, normal));
+                                                                       item->xform_dist *= scale;
+                                                               }
+
+                                                               float target_normal[3];
+                                                               copy_v3_v3(target_normal, normal);
+
+#ifdef USE_RELATIVE_ROTATION
+                                                               if (i != 0) {
+                                                                       mul_m3_v3(item->xform_rot_offset, target_normal);
+                                                               }
+#endif
+                                                               {
+                                                                       float loc[3];
+
+                                                                       copy_v3_v3(loc, location_world);
+                                                                       madd_v3_v3fl(loc, target_normal, item->xform_dist);
+                                                                       object_apply_location(item->ob, loc);
+                                                                       copy_v3_v3(item->ob->obmat[3], loc);  /* so orient behaves as expected */
+                                                               }
+
+                                                               object_orient_to_location(item->ob, item->rot_mat, item->rot_mat[2], location_world);
+                                                               WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob);
+                                                       }
+                                                       copy_v3_v3(xfd->prev.normal, normal);
+                                                       xfd->prev.is_normal_valid = true;
+                                               }
+                                       }
+                                       else {
+                                               struct XFormAxisItem *item = xfd->object_data;
+                                               for (int i = 0; i < xfd->object_data_len; i++, item++) {
+                                                       object_orient_to_location(item->ob, item->rot_mat, item->rot_mat[2], location_world);
+                                                       WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob);
+                                               }
+                                               xfd->prev.is_normal_valid = false;
+                                       }
+                               }
+                       }
+               }
+               xfd->is_translate = is_translate;
+
+               ED_region_tag_redraw(xfd->vc.ar);
+       }
+
+       bool is_finished = false;
+
+       if (ISMOUSE(xfd->init_event)) {
+               if ((event->type == xfd->init_event) && (event->val == KM_RELEASE)) {
+                       is_finished = true;
+               }
+       }
+       else {
+               if (ELEM(event->type, LEFTMOUSE, RETKEY, PADENTER)) {
+                       is_finished = true;
+               }
+       }
+
+       if (is_finished) {
+               object_transform_axis_target_free_data(op);
+               return OPERATOR_FINISHED;
+       }
+       else if (ELEM(event->type, ESCKEY, RIGHTMOUSE)) {
+               object_transform_axis_target_cancel(C, op);
+               return OPERATOR_CANCELLED;
+       }
+
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+void OBJECT_OT_transform_axis_target(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Interactive Lamp Track to Cursor";
+       ot->description = "Interactively point cameras and lamps to a location (Ctrl translates)";
+       ot->idname = "OBJECT_OT_transform_axis_target";
+
+       /* api callbacks */
+       ot->invoke = object_transform_axis_target_invoke;
+       ot->cancel = object_transform_axis_target_cancel;
+       ot->modal = object_transform_axis_target_modal;
+       ot->poll = ED_operator_region_view3d_active;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+}
+
+#undef USE_RELATIVE_ROTATION
+
+/** \} */