diff options
Diffstat (limited to 'source/blender/editors/object')
-rw-r--r-- | source/blender/editors/object/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_edit.c | 192 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 2 | ||||
-rw-r--r-- | source/blender/editors/object/object_modifier.c | 151 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 2 | ||||
-rw-r--r-- | source/blender/editors/object/object_transform.c | 22 |
6 files changed, 306 insertions, 64 deletions
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index c2c25e47908..de9cc72dfa4 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -34,6 +34,7 @@ set(INC ../../shader_fx ../../render/extern/include ../../windowmanager + ../../../../intern/clog ../../../../intern/glew-mx ../../../../intern/guardedalloc ) diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 5506895613b..d944d4c4dd3 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -100,6 +100,8 @@ #include "RNA_define.h" #include "RNA_enum_types.h" +#include "CLG_log.h" + /* for menu/popup icons etc etc*/ #include "UI_interface.h" @@ -112,21 +114,17 @@ #include "object_intern.h" // own include +static CLG_LogRef LOG = {"ed.object.edit"}; + /* prototypes */ typedef struct MoveToCollectionData MoveToCollectionData; static void move_to_collection_menus_items(struct uiLayout *layout, struct MoveToCollectionData *menu); static ListBase selected_objects_get(bContext *C); -/* ************* XXX **************** */ -static void error(const char *UNUSED(arg)) -{ -} - -/* port over here */ -static void error_libdata(void) -{ -} +/* -------------------------------------------------------------------- */ +/** \name Internal Utilities + * \{ */ Object *ED_object_context(bContext *C) { @@ -147,7 +145,11 @@ Object *ED_object_active_context(bContext *C) return ob; } -/* ********************** object hiding *************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Hide Operator + * \{ */ static bool object_hide_poll(bContext *C) { @@ -401,7 +403,11 @@ void OBJECT_OT_hide_collection(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } -/* ******************* toggle editmode operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Edit-Mode Operator + * \{ */ static bool mesh_needs_keyindex(Main *bmain, const Mesh *me) { @@ -441,7 +447,11 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f } if (me->edit_mesh->bm->totvert > MESH_MAX_VERTS) { - error("Too many vertices"); + /* This used to be warned int the UI, we could warn again although it's quite rare. */ + CLOG_WARN(&LOG, + "Too many vertices for mesh '%s' (%d)", + me->id.name + 2, + me->edit_mesh->bm->totvert); return false; } @@ -600,7 +610,8 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag } if (BKE_object_obdata_is_libdata(ob)) { - error_libdata(); + /* Ideally the caller should check this. */ + CLOG_WARN(&LOG, "Unable to enter edit-mode on library data for object '%s'", ob->id.name + 2); return false; } @@ -777,7 +788,11 @@ void OBJECT_OT_editmode_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* *************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Pose-Mode Operator + * \{ */ static int posemode_exec(bContext *C, wmOperator *op) { @@ -861,7 +876,11 @@ void OBJECT_OT_posemode_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ******************* force field toggle operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Force Field Toggle Operator + * \{ */ void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object) { @@ -924,8 +943,11 @@ void OBJECT_OT_forcefield_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************************************** */ -/* Motion Paths */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Calculate Motion Paths Operator + * \{ */ static eAnimvizCalcRange object_path_convert_range(eObjectPathCalcRange range) { @@ -1019,7 +1041,7 @@ static int object_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEv } /* show popup dialog to allow editing of range... */ - /* FIXME: hardcoded dimensions here are just arbitrary */ + /* FIXME: hard-coded dimensions here are just arbitrary. */ return WM_operator_props_dialog_popup(C, op, 200); } @@ -1088,7 +1110,11 @@ void OBJECT_OT_paths_calculate(wmOperatorType *ot) MAXFRAME / 2.0); } -/* --------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Update Motion Paths Operator + * \{ */ static bool object_update_paths_poll(bContext *C) { @@ -1132,7 +1158,11 @@ void OBJECT_OT_paths_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* --------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Motion Paths Operator + * \{ */ /* Helper for ED_objects_clear_paths() */ static void object_clear_mpath(Object *ob) @@ -1151,14 +1181,14 @@ static void object_clear_mpath(Object *ob) void ED_objects_clear_paths(bContext *C, bool only_selected) { if (only_selected) { - /* loop over all selected + sedtiable objects in scene */ + /* Loop over all selected + editable objects in scene. */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { object_clear_mpath(ob); } CTX_DATA_END; } else { - /* loop over all edtiable objects in scene */ + /* Loop over all editable objects in scene. */ CTX_DATA_BEGIN (C, Object *, ob, editable_objects) { object_clear_mpath(ob); } @@ -1210,13 +1240,17 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } -/* --------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Update Motion Paths Range from Scene Operator + * \{ */ static int object_update_paths_range_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - /* loop over all edtiable objects in scene */ + /* Loop over all editable objects in scene. */ CTX_DATA_BEGIN (C, Object *, ob, editable_objects) { /* use Preview Range or Full Frame Range - whichever is in use */ ob->avs.path_sf = PSFRA; @@ -1246,63 +1280,99 @@ void OBJECT_OT_paths_range_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Smooth/Flat *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Shade Smooth/Flat Operator + * \{ */ static int shade_smooth_exec(bContext *C, wmOperator *op) { - ID *data; - Curve *cu; - Nurb *nu; - int clear = (STREQ(op->idname, "OBJECT_OT_shade_flat")); - bool done = false, linked_data = false; + const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth"); + bool changed_multi = false; + bool has_linked_data = false; - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - data = ob->data; + ListBase ctx_objects = {NULL, NULL}; + CollectionPointerLink ctx_ob_single_active = {NULL}; + + /* For modes that only use an active object, don't handle the whole selection. */ + { + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obact = OBACT(view_layer); + if (obact && ((obact->mode & OB_MODE_ALL_PAINT))) { + ctx_ob_single_active.ptr.data = obact; + BLI_addtail(&ctx_objects, &ctx_ob_single_active); + } + } + + if (ctx_objects.first != &ctx_ob_single_active) { + CTX_data_selected_editable_objects(C, &ctx_objects); + } + + for (CollectionPointerLink *ctx_ob = ctx_objects.first; ctx_ob; ctx_ob = ctx_ob->next) { + Object *ob = ctx_ob->ptr.data; + ID *data = ob->data; + if (data != NULL) { + data->tag |= LIB_TAG_DOIT; + } + } + + for (CollectionPointerLink *ctx_ob = ctx_objects.first; ctx_ob; ctx_ob = ctx_ob->next) { + /* Always un-tag all object data-blocks irrespective of our ability to operate on them. */ + Object *ob = ctx_ob->ptr.data; + ID *data = ob->data; + if ((data == NULL) || ((data->tag & LIB_TAG_DOIT) == 0)) { + continue; + } + data->tag &= ~LIB_TAG_DOIT; + /* Finished un-tagging, continue with regular logic. */ if (data && ID_IS_LINKED(data)) { - linked_data = true; + has_linked_data = true; continue; } + bool changed = false; if (ob->type == OB_MESH) { - BKE_mesh_smooth_flag_set(ob->data, !clear); - + BKE_mesh_smooth_flag_set(ob->data, use_smooth); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - done = true; + changed = true; } else if (ELEM(ob->type, OB_SURF, OB_CURVE)) { - cu = ob->data; + BKE_curve_smooth_flag_set(ob->data, use_smooth); + changed = true; + } - for (nu = cu->nurb.first; nu; nu = nu->next) { - if (!clear) { - nu->flag |= ME_SMOOTH; - } - else { - nu->flag &= ~ME_SMOOTH; - } - } + if (changed) { + changed_multi = true; DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - done = true; } } - CTX_DATA_END; - if (linked_data) { + if (ctx_objects.first != &ctx_ob_single_active) { + BLI_freelistN(&ctx_objects); + } + + if (has_linked_data) { BKE_report(op->reports, RPT_WARNING, "Can't edit linked mesh or curve data"); } - return (done) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + return (changed_multi) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } static bool shade_poll(bContext *C) { - return (CTX_data_edit_object(C) == NULL); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obact = OBACT(view_layer); + if (obact != NULL) { + /* Doesn't handle edit-data, sculpt dynamic-topology, or their undo systems. */ + if (obact->mode & (OB_MODE_EDIT | OB_MODE_SCULPT)) { + return false; + } + } + return true; } void OBJECT_OT_shade_flat(wmOperatorType *ot) @@ -1335,7 +1405,11 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Mode Set Operator + * \{ */ static const EnumPropertyItem *object_mode_set_itemsf(bContext *C, PointerRNA *UNUSED(ptr), @@ -1516,6 +1590,12 @@ void OBJECT_OT_mode_set_with_submode(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Link/Move to Collection Operator + * \{ */ + static ListBase selected_objects_get(bContext *C) { ListBase objects = {NULL}; @@ -1856,3 +1936,5 @@ void OBJECT_OT_link_to_collection(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); ot->prop = prop; } + +/** \} */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index d8ba270073e..d7a7b4ca110 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -169,6 +169,8 @@ void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot); void OBJECT_OT_multires_reshape(struct wmOperatorType *ot); void OBJECT_OT_multires_higher_levels_delete(struct wmOperatorType *ot); void OBJECT_OT_multires_base_apply(struct wmOperatorType *ot); +void OBJECT_OT_multires_unsubdivide(struct wmOperatorType *ot); +void OBJECT_OT_multires_rebuild_subdiv(struct wmOperatorType *ot); void OBJECT_OT_multires_external_save(struct wmOperatorType *ot); void OBJECT_OT_multires_external_pack(struct wmOperatorType *ot); void OBJECT_OT_correctivesmooth_bind(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index a24f3ba2269..bcc5bf3e1ae 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -142,10 +142,11 @@ static void object_force_modifier_bind_simple_options(Depsgraph *depsgraph, md_eval->mode = mode; } -/** Add a modifier to given object, including relevant extra processing needed by some physics - * types (particles, simulations...). +/** + * Add a modifier to given object, including relevant extra processing needed by some physics types + * (particles, simulations...). * - * \param scene is only used to set current frame in some cases, and may be NULL. + * \param scene: is only used to set current frame in some cases, and may be NULL. */ ModifierData *ED_object_modifier_add( ReportList *reports, Main *bmain, Scene *scene, Object *ob, const char *name, int type) @@ -1432,6 +1433,25 @@ void OBJECT_OT_multires_higher_levels_delete(wmOperatorType *ot) /** \name Multires Subdivide Operator * \{ */ +static EnumPropertyItem prop_multires_subdivide_mode_type[] = { + {MULTIRES_SUBDIVIDE_CATMULL_CLARK, + "CATMULL_CLARK", + 0, + "Catmull-Clark", + "Create a new level using Catmull-Clark subdivisions"}, + {MULTIRES_SUBDIVIDE_SIMPLE, + "SIMPLE", + 0, + "Simple", + "Create a new level using simple subdivisions"}, + {MULTIRES_SUBDIVIDE_LINEAR, + "LINEAR", + 0, + "Linear", + "Create a new level using linear interpolation of the sculpted displacement"}, + {0, NULL, 0, NULL, NULL}, +}; + static int multires_subdivide_exec(bContext *C, wmOperator *op) { Object *object = ED_object_active_context(C); @@ -1442,7 +1462,9 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - multiresModifier_subdivide(object, mmd); + const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)( + RNA_enum_get(op->ptr, "mode")); + multiresModifier_subdivide(object, mmd, subdivide_mode); ED_object_iter_other( CTX_data_main(C), object, true, ED_object_multires_update_totlevels_cb, &mmd->totlvl); @@ -1481,6 +1503,12 @@ void OBJECT_OT_multires_subdivide(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); + RNA_def_enum(ot->srna, + "mode", + prop_multires_subdivide_mode_type, + MULTIRES_SUBDIVIDE_CATMULL_CLARK, + "Subdivision Mode", + "How the mesh is going to be subdivided to create a new level"); } /** \} */ @@ -1738,6 +1766,119 @@ void OBJECT_OT_multires_base_apply(wmOperatorType *ot) /** \} */ /* ------------------------------------------------------------------- */ +/** \name Multires Unsubdivide + * \{ */ + +static int multires_unsubdivide_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *object = ED_object_active_context(C); + MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get( + op, object, eModifierType_Multires); + + if (!mmd) { + return OPERATOR_CANCELLED; + } + + int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, 1, true); + if (new_levels == 0) { + BKE_report(op->reports, RPT_ERROR, "Not valid subdivisions found to rebuild a lower level"); + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object); + + return OPERATOR_FINISHED; +} + +static int multires_unsubdivide_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_modifier_invoke_properties(C, op)) { + return multires_unsubdivide_exec(C, op); + } + else { + return OPERATOR_CANCELLED; + } +} + +void OBJECT_OT_multires_unsubdivide(wmOperatorType *ot) +{ + ot->name = "Unsubdivide"; + ot->description = "Rebuild a lower subdivision level of the current base mesh"; + ot->idname = "OBJECT_OT_multires_unsubdivide"; + + ot->poll = multires_poll; + ot->invoke = multires_unsubdivide_invoke; + ot->exec = multires_unsubdivide_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Multires Rebuild Subdivisions + * \{ */ + +static int multires_rebuild_subdiv_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *object = ED_object_active_context(C); + MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get( + op, object, eModifierType_Multires); + + if (!mmd) { + return OPERATOR_CANCELLED; + } + + int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, INT_MAX, false); + if (new_levels == 0) { + BKE_report(op->reports, RPT_ERROR, "Not valid subdivisions found to rebuild lower levels"); + return OPERATOR_CANCELLED; + } + + BKE_reportf(op->reports, RPT_INFO, "%d new levels rebuilt", new_levels); + + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object); + + return OPERATOR_FINISHED; +} + +static int multires_rebuild_subdiv_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + if (edit_modifier_invoke_properties(C, op)) { + return multires_rebuild_subdiv_exec(C, op); + } + else { + return OPERATOR_CANCELLED; + } +} + +void OBJECT_OT_multires_rebuild_subdiv(wmOperatorType *ot) +{ + ot->name = "Rebuild Lower Subdivisions"; + ot->description = + "Rebuilds all possible subdivisions levels to generate a lower resolution base mesh"; + ot->idname = "OBJECT_OT_multires_rebuild_subdiv"; + + ot->poll = multires_poll; + ot->invoke = multires_rebuild_subdiv_invoke; + ot->exec = multires_rebuild_subdiv_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ /** \name Skin Modifier * \{ */ @@ -2455,7 +2596,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) * No drivers or other modifier parameters. */ /* TODO(sergey): This operates on an original data, so no flush is needed. However, baking * usually should happen on an evaluated objects, so this seems to be deeper issue here. */ - BKE_animsys_evaluate_animdata(scene, (ID *)ob, ob->adt, f, ADT_RECALC_ANIM, false); + BKE_animsys_evaluate_animdata((ID *)ob, ob->adt, f, ADT_RECALC_ANIM, false); och->time[i] = omd->time; i++; diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index fef046169a7..819b6c18a44 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -137,6 +137,8 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_multires_reshape); WM_operatortype_append(OBJECT_OT_multires_higher_levels_delete); WM_operatortype_append(OBJECT_OT_multires_base_apply); + WM_operatortype_append(OBJECT_OT_multires_unsubdivide); + WM_operatortype_append(OBJECT_OT_multires_rebuild_subdiv); WM_operatortype_append(OBJECT_OT_multires_external_save); WM_operatortype_append(OBJECT_OT_multires_external_pack); WM_operatortype_append(OBJECT_OT_skin_root_mark); diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 62a8b46e904..132b530455e 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1608,6 +1608,7 @@ struct XFormAxisItem { float rot_mat[3][3]; void *obtfm; float xform_dist; + bool is_z_flip; #ifdef USE_RELATIVE_ROTATION /* use when translating multiple */ @@ -1730,11 +1731,16 @@ static void object_apply_location(Object *ob, const float loc[3]) static bool object_orient_to_location(Object *ob, const float rot_orig[3][3], const float axis[3], - const float location[3]) + const float location[3], + const bool z_flip) { float delta[3]; sub_v3_v3v3(delta, ob->obmat[3], location); if (normalize_v3(delta) != 0.0f) { + if (z_flip) { + negate_v3(delta); + } + if (len_squared_v3v3(delta, axis) > FLT_EPSILON) { float delta_rot[3][3]; float final_rot[3][3]; @@ -1841,6 +1847,11 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons 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); + + /* Detect negative scale matrix. */ + float full_mat3[3][3]; + BKE_object_to_mat3(item->ob, full_mat3); + item->is_z_flip = dot_v3v3(item->rot_mat[2], full_mat3[2]) < 0.0f; } } @@ -1975,7 +1986,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const } object_orient_to_location( - item->ob, item->rot_mat, item->rot_mat[2], location_world); + item->ob, item->rot_mat, item->rot_mat[2], location_world, item->is_z_flip); DEG_id_tag_update(&item->ob->id, ID_RECALC_TRANSFORM); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob); @@ -1989,8 +2000,11 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const else { struct XFormAxisItem *item = xfd->object_data; for (int i = 0; i < xfd->object_data_len; i++, item++) { - if (object_orient_to_location( - item->ob, item->rot_mat, item->rot_mat[2], location_world)) { + if (object_orient_to_location(item->ob, + item->rot_mat, + item->rot_mat[2], + location_world, + item->is_z_flip)) { DEG_id_tag_update(&item->ob->id, ID_RECALC_TRANSFORM); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, item->ob); } |