From 3ba5461af0c8b1273d3daf87d050c24a690273be Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 1 Feb 2020 13:34:27 +1100 Subject: Object: support 'Affect Parents' for snap/clear transform Resolves T69450 --- source/blender/editors/include/ED_object.h | 55 +++- .../blender/editors/object/object_data_transform.c | 97 ------- source/blender/editors/object/object_transform.c | 42 ++- source/blender/editors/object/object_utils.c | 289 +++++++++++++++++++++ source/blender/editors/space_view3d/view3d_snap.c | 90 +++++-- .../editors/transform/transform_convert_object.c | 146 +---------- 6 files changed, 452 insertions(+), 267 deletions(-) diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index d27eeb270ac..a3b7d6bd1c1 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -73,6 +73,51 @@ bool ED_object_calc_active_center_for_posemode(struct Object *ob, float r_center[3]); bool ED_object_calc_active_center(struct Object *ob, const bool select_only, float r_center[3]); +/* Object Data Container helper API. */ +struct XFormObjectData_Container; +struct XFormObjectData_Container *ED_object_data_xform_container_create(void); +void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xds); +void ED_object_data_xform_container_update_all(struct XFormObjectData_Container *xds, + struct Main *bmain, + struct Depsgraph *depsgraph); +void ED_object_data_xform_container_item_ensure(struct XFormObjectData_Container *xds, + struct Object *ob); + +/* Object Skip-Child Container helper API. */ +enum { + /** + * The parent is transformed, this is held in place. + */ + XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM = 1, + /** + * The same as #XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM, + * however this objects parent isn't transformed directly. + */ + XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT = 3, + /** + * Use the parent invert matrix to apply transformation, + * this is needed, because breaks in the selection chain prevents this from being transformed. + * This is used to add the transform which would have been added + * if there weren't breaks in the parent/child chain. + */ + XFORM_OB_SKIP_CHILD_PARENT_APPLY = 2, +}; +struct XFormObjectSkipChild_Container; +struct XFormObjectSkipChild_Container *ED_object_xform_skip_child_container_create(void); +void ED_object_xform_skip_child_container_item_ensure_from_array( + struct XFormObjectSkipChild_Container *xcs, + struct ViewLayer *view_layer, + struct Object **objects, + uint objects_len); +void ED_object_xform_skip_child_container_destroy(struct XFormObjectSkipChild_Container *xcs); +void ED_object_xform_skip_child_container_update_all(struct XFormObjectSkipChild_Container *xcs, + struct Main *bmain, + struct Depsgraph *depsgraph); +void ED_object_xform_skip_child_container_item_ensure(struct XFormObjectSkipChild_Container *xcs, + struct Object *ob, + struct Object *ob_parent_recurse, + int mode); + /* object_ops.c */ void ED_operatortypes_object(void); void ED_operatormacros_object(void); @@ -421,16 +466,6 @@ void ED_object_data_xform_by_mat4(struct XFormObjectData *xod, const float mat[4 void ED_object_data_xform_restore(struct XFormObjectData *xod); void ED_object_data_xform_tag_update(struct XFormObjectData *xod); -/* Container helper API. */ -struct XFormObjectData_Container; -struct XFormObjectData_Container *ED_object_data_xform_container_create(void); -void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xds); -void ED_object_data_xform_container_update_all(struct XFormObjectData_Container *xds, - struct Main *bmain, - struct Depsgraph *depsgraph); -void ED_object_data_xform_container_item_ensure(struct XFormObjectData_Container *xds, - struct Object *ob); - #ifdef __cplusplus } #endif diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c index b6f125c6f71..6d03ec2774c 100644 --- a/source/blender/editors/object/object_data_transform.c +++ b/source/blender/editors/object/object_data_transform.c @@ -580,100 +580,3 @@ void ED_object_data_xform_tag_update(struct XFormObjectData *xod_base) } /** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Object Data Transform Container - * - * Use to implement 'Affect Only Origins' feature. - * - * \{ */ - -struct XFormObjectData_Container { - GHash *obdata_in_obmode_map; -}; - -struct XFormObjectData_Extra { - Object *ob; - float obmat_orig[4][4]; - struct XFormObjectData *xod; -}; - -void ED_object_data_xform_container_item_ensure(struct XFormObjectData_Container *xds, Object *ob) -{ - if (xds->obdata_in_obmode_map == NULL) { - xds->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__); - } - - void **xf_p; - if (!BLI_ghash_ensure_p(xds->obdata_in_obmode_map, ob->data, &xf_p)) { - struct XFormObjectData_Extra *xf = MEM_mallocN(sizeof(*xf), __func__); - copy_m4_m4(xf->obmat_orig, ob->obmat); - xf->ob = ob; - /* Result may be NULL, that's OK. */ - xf->xod = ED_object_data_xform_create(ob->data); - *xf_p = xf; - } -} - -/** - * This may be called multiple times with the same data. - * Each time, the original transformations are re-applied, instead of accumulating the changes. - */ -void ED_object_data_xform_container_update_all(struct XFormObjectData_Container *xds, - struct Main *bmain, - Depsgraph *depsgraph) -{ - if (xds->obdata_in_obmode_map == NULL) { - return; - } - BKE_scene_graph_evaluated_ensure(depsgraph, bmain); - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, xds->obdata_in_obmode_map) { - ID *id = BLI_ghashIterator_getKey(&gh_iter); - struct XFormObjectData_Extra *xf = BLI_ghashIterator_getValue(&gh_iter); - if (xf->xod == NULL) { - continue; - } - - Object *ob_eval = DEG_get_evaluated_object(depsgraph, xf->ob); - float imat[4][4], dmat[4][4]; - invert_m4_m4(imat, xf->obmat_orig); - mul_m4_m4m4(dmat, imat, ob_eval->obmat); - invert_m4(dmat); - - ED_object_data_xform_by_mat4(xf->xod, dmat); - if (xf->ob->type == OB_ARMATURE) { - /* TODO: none of the current flags properly update armatures, needs investigation. */ - DEG_id_tag_update(id, 0); - } - else { - DEG_id_tag_update(id, ID_RECALC_GEOMETRY); - } - } -} - -/** Callback for #GHash free. */ -static void trans_obdata_in_obmode_free_elem(void *xf_p) -{ - struct XFormObjectData_Extra *xf = xf_p; - if (xf->xod) { - ED_object_data_xform_destroy(xf->xod); - } - MEM_freeN(xf); -} - -struct XFormObjectData_Container *ED_object_data_xform_container_create(void) -{ - struct XFormObjectData_Container *xds = MEM_callocN(sizeof(*xds), __func__); - xds->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__); - return xds; -} - -void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xds) -{ - BLI_ghash_free(xds->obdata_in_obmode_map, NULL, trans_obdata_in_obmode_free_elem); - MEM_freeN(xds); -} - -/** \} */ diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 26fd66c7f3e..4f56664ec99 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -44,6 +44,7 @@ #include "BKE_curve.h" #include "BKE_main.h" #include "BKE_idcode.h" +#include "BKE_layer.h" #include "BKE_mball.h" #include "BKE_mesh.h" #include "BKE_object.h" @@ -296,22 +297,43 @@ static int object_clear_transform_generic_exec(bContext *C, Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + /* May be NULL. */ + View3D *v3d = CTX_wm_view3d(C); KeyingSet *ks; const bool clear_delta = RNA_boolean_get(op->ptr, "clear_delta"); - /* sanity checks */ - if (ELEM(NULL, clear_func, default_ksName)) { - BKE_report(op->reports, - RPT_ERROR, - "Programming error: missing clear transform function or keying set name"); + BLI_assert(!ELEM(NULL, clear_func, default_ksName)); + + Object **objects = NULL; + uint objects_len = 0; + { + BLI_array_declare(objects); + FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer, v3d, ob) { + BLI_array_append(objects, ob); + } + FOREACH_SELECTED_EDITABLE_OBJECT_END; + objects_len = BLI_array_len(objects); + } + + if (objects == NULL) { return OPERATOR_CANCELLED; } /* Support transforming the object data. */ + const bool use_transform_skip_children = (scene->toolsettings->transform_flag & + SCE_XFORM_SKIP_CHILDREN); const bool use_transform_data_origin = (scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN); + struct XFormObjectSkipChild_Container *xcs = NULL; struct XFormObjectData_Container *xds = NULL; + if (use_transform_skip_children) { + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + xcs = ED_object_xform_skip_child_container_create(); + ED_object_xform_skip_child_container_item_ensure_from_array( + xcs, view_layer, objects, objects_len); + } if (use_transform_data_origin) { BKE_scene_graph_evaluated_ensure(depsgraph, bmain); xds = ED_object_data_xform_container_create(); @@ -320,7 +342,8 @@ static int object_clear_transform_generic_exec(bContext *C, /* get KeyingSet to use */ ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName); - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; if (use_transform_data_origin) { ED_object_data_xform_container_item_ensure(xds, ob); @@ -334,7 +357,12 @@ static int object_clear_transform_generic_exec(bContext *C, /* tag for updates */ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } - CTX_DATA_END; + MEM_freeN(objects); + + if (use_transform_skip_children) { + ED_object_xform_skip_child_container_update_all(xcs, bmain, depsgraph); + ED_object_xform_skip_child_container_destroy(xcs); + } if (use_transform_data_origin) { ED_object_data_xform_container_update_all(xds, bmain, depsgraph); diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c index dbca0bbf97b..ed764f4dcfc 100644 --- a/source/blender/editors/object/object_utils.c +++ b/source/blender/editors/object/object_utils.c @@ -34,6 +34,9 @@ #include "BKE_action.h" #include "BKE_editmesh.h" #include "BKE_lattice.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph_query.h" #include "WM_types.h" @@ -41,6 +44,8 @@ #include "ED_curve.h" #include "ED_object.h" /* own include */ +#include "MEM_guardedalloc.h" + /* -------------------------------------------------------------------- */ /** \name Active Element Center * \{ */ @@ -142,3 +147,287 @@ bool ED_object_calc_active_center(Object *ob, const bool select_only, float r_ce } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Child Skip + * + * Don't transform unselected children, this is done using the parent inverse matrix. + * + * \note The complex logic here is caused by mixed selection within a single selection chain, + * otherwise we only need #XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM for single objects. + * + * \{ */ + +struct XFormObjectSkipChild_Container { + GHash *obchild_in_obmode_map; +}; + +struct XFormObjectSkipChild { + float obmat_orig[4][4]; + float parent_obmat_orig[4][4]; + float parent_obmat_inv_orig[4][4]; + float parent_recurse_obmat_orig[4][4]; + float parentinv_orig[4][4]; + Object *ob_parent_recurse; + int mode; +}; + +struct XFormObjectSkipChild_Container *ED_object_xform_skip_child_container_create(void) +{ + struct XFormObjectSkipChild_Container *xcs = MEM_callocN(sizeof(*xcs), __func__); + if (xcs->obchild_in_obmode_map == NULL) { + xcs->obchild_in_obmode_map = BLI_ghash_ptr_new(__func__); + } + return xcs; +} + +void ED_object_xform_skip_child_container_item_ensure_from_array( + struct XFormObjectSkipChild_Container *xcs, + ViewLayer *view_layer, + Object **objects, + uint objects_len) +{ + GSet *objects_in_transdata = BLI_gset_ptr_new_ex(__func__, objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BLI_gset_add(objects_in_transdata, ob); + } + + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + if (ob->parent != NULL) { + if (!BLI_gset_haskey(objects_in_transdata, ob)) { + if (BLI_gset_haskey(objects_in_transdata, ob->parent)) { + ED_object_xform_skip_child_container_item_ensure( + xcs, ob, NULL, XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM); + } + } + else { + if (!BLI_gset_haskey(objects_in_transdata, ob->parent)) { + Object *ob_parent_recurse = ob->parent; + if (ob_parent_recurse != NULL) { + while (ob_parent_recurse != NULL) { + if (BLI_gset_haskey(objects_in_transdata, ob_parent_recurse)) { + break; + } + ob_parent_recurse = ob_parent_recurse->parent; + } + + if (ob_parent_recurse) { + ED_object_xform_skip_child_container_item_ensure( + xcs, ob, ob_parent_recurse, XFORM_OB_SKIP_CHILD_PARENT_APPLY); + } + } + } + } + } + } + + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + + if (BLI_gset_haskey(objects_in_transdata, ob)) { + /* pass. */ + } + else if (ob->parent != NULL) { + if (BLI_gset_haskey(objects_in_transdata, ob->parent)) { + if (!BLI_gset_haskey(objects_in_transdata, ob)) { + ED_object_xform_skip_child_container_item_ensure( + xcs, ob, NULL, XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM); + } + } + } + } + BLI_gset_free(objects_in_transdata, NULL); +} + +void ED_object_xform_skip_child_container_destroy(struct XFormObjectSkipChild_Container *xcs) +{ + BLI_ghash_free(xcs->obchild_in_obmode_map, NULL, MEM_freeN); + MEM_freeN(xcs); +} + +void ED_object_xform_skip_child_container_item_ensure(struct XFormObjectSkipChild_Container *xcs, + Object *ob, + Object *ob_parent_recurse, + int mode) +{ + void **xf_p; + if (!BLI_ghash_ensure_p(xcs->obchild_in_obmode_map, ob, &xf_p)) { + struct XFormObjectSkipChild *xf = MEM_mallocN(sizeof(*xf), __func__); + copy_m4_m4(xf->parentinv_orig, ob->parentinv); + copy_m4_m4(xf->obmat_orig, ob->obmat); + copy_m4_m4(xf->parent_obmat_orig, ob->parent->obmat); + invert_m4_m4(xf->parent_obmat_inv_orig, ob->parent->obmat); + if (ob_parent_recurse) { + copy_m4_m4(xf->parent_recurse_obmat_orig, ob_parent_recurse->obmat); + } + xf->mode = mode; + xf->ob_parent_recurse = ob_parent_recurse; + *xf_p = xf; + } +} + +void ED_object_xform_skip_child_container_update_all(struct XFormObjectSkipChild_Container *xcs, + struct Main *bmain, + struct Depsgraph *depsgraph) +{ + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, xcs->obchild_in_obmode_map) { + Object *ob = BLI_ghashIterator_getKey(&gh_iter); + struct XFormObjectSkipChild *xf = BLI_ghashIterator_getValue(&gh_iter); + + /* The following blocks below assign 'dmat'. */ + float dmat[4][4]; + + if (xf->mode == XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM) { + /* Parent is transformed, this isn't so compensate. */ + Object *ob_parent_eval = DEG_get_evaluated_object(depsgraph, ob->parent); + mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, ob_parent_eval->obmat); + invert_m4(dmat); + } + else if (xf->mode == XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT) { + /* Calculate parent matrix (from the root transform). */ + Object *ob_parent_recurse_eval = DEG_get_evaluated_object(depsgraph, xf->ob_parent_recurse); + float parent_recurse_obmat_inv[4][4]; + invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat); + mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv); + invert_m4(dmat); + float parent_obmat_calc[4][4]; + mul_m4_m4m4(parent_obmat_calc, dmat, xf->parent_obmat_orig); + + /* Apply to the parent inverse matrix. */ + mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, parent_obmat_calc); + invert_m4(dmat); + } + else { + BLI_assert(xf->mode == XFORM_OB_SKIP_CHILD_PARENT_APPLY); + /* Transform this - without transform data. */ + Object *ob_parent_recurse_eval = DEG_get_evaluated_object(depsgraph, xf->ob_parent_recurse); + float parent_recurse_obmat_inv[4][4]; + invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat); + mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv); + invert_m4(dmat); + float obmat_calc[4][4]; + mul_m4_m4m4(obmat_calc, dmat, xf->obmat_orig); + /* obmat_calc is just obmat. */ + + /* Get the matrices relative to the parent. */ + float obmat_parent_relative_orig[4][4]; + float obmat_parent_relative_calc[4][4]; + float obmat_parent_relative_inv_orig[4][4]; + + mul_m4_m4m4(obmat_parent_relative_orig, xf->parent_obmat_inv_orig, xf->obmat_orig); + mul_m4_m4m4(obmat_parent_relative_calc, xf->parent_obmat_inv_orig, obmat_calc); + invert_m4_m4(obmat_parent_relative_inv_orig, obmat_parent_relative_orig); + + /* Apply to the parent inverse matrix. */ + mul_m4_m4m4(dmat, obmat_parent_relative_calc, obmat_parent_relative_inv_orig); + } + + mul_m4_m4m4(ob->parentinv, dmat, xf->parentinv_orig); + + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Data Transform Container + * + * Use to implement 'Affect Only Origins' feature. + * + * \{ */ + +struct XFormObjectData_Container { + GHash *obdata_in_obmode_map; +}; + +struct XFormObjectData_Extra { + Object *ob; + float obmat_orig[4][4]; + struct XFormObjectData *xod; +}; + +void ED_object_data_xform_container_item_ensure(struct XFormObjectData_Container *xds, Object *ob) +{ + if (xds->obdata_in_obmode_map == NULL) { + xds->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__); + } + + void **xf_p; + if (!BLI_ghash_ensure_p(xds->obdata_in_obmode_map, ob->data, &xf_p)) { + struct XFormObjectData_Extra *xf = MEM_mallocN(sizeof(*xf), __func__); + copy_m4_m4(xf->obmat_orig, ob->obmat); + xf->ob = ob; + /* Result may be NULL, that's OK. */ + xf->xod = ED_object_data_xform_create(ob->data); + *xf_p = xf; + } +} + +/** + * This may be called multiple times with the same data. + * Each time, the original transformations are re-applied, instead of accumulating the changes. + */ +void ED_object_data_xform_container_update_all(struct XFormObjectData_Container *xds, + struct Main *bmain, + Depsgraph *depsgraph) +{ + if (xds->obdata_in_obmode_map == NULL) { + return; + } + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, xds->obdata_in_obmode_map) { + ID *id = BLI_ghashIterator_getKey(&gh_iter); + struct XFormObjectData_Extra *xf = BLI_ghashIterator_getValue(&gh_iter); + if (xf->xod == NULL) { + continue; + } + + Object *ob_eval = DEG_get_evaluated_object(depsgraph, xf->ob); + float imat[4][4], dmat[4][4]; + invert_m4_m4(imat, xf->obmat_orig); + mul_m4_m4m4(dmat, imat, ob_eval->obmat); + invert_m4(dmat); + + ED_object_data_xform_by_mat4(xf->xod, dmat); + if (xf->ob->type == OB_ARMATURE) { + /* TODO: none of the current flags properly update armatures, needs investigation. */ + DEG_id_tag_update(id, 0); + } + else { + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); + } + } +} + +/** Callback for #GHash free. */ +static void trans_obdata_in_obmode_free_elem(void *xf_p) +{ + struct XFormObjectData_Extra *xf = xf_p; + if (xf->xod) { + ED_object_data_xform_destroy(xf->xod); + } + MEM_freeN(xf); +} + +struct XFormObjectData_Container *ED_object_data_xform_container_create(void) +{ + struct XFormObjectData_Container *xds = MEM_callocN(sizeof(*xds), __func__); + xds->obdata_in_obmode_map = BLI_ghash_ptr_new(__func__); + return xds; +} + +void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xds) +{ + BLI_ghash_free(xds->obdata_in_obmode_map, NULL, trans_obdata_in_obmode_free_elem); + MEM_freeN(xds); +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index f13f41779c2..33930efdd52 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -26,6 +26,7 @@ #include "DNA_armature_types.h" #include "DNA_object_types.h" +#include "BLI_array.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_math.h" @@ -188,16 +189,47 @@ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op)) struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID); + const bool use_transform_skip_children = (scene->toolsettings->transform_flag & + SCE_XFORM_SKIP_CHILDREN); const bool use_transform_data_origin = (scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN); + struct XFormObjectSkipChild_Container *xcs = NULL; struct XFormObjectData_Container *xds = NULL; + /* Build object array. */ + Object **objects_eval = NULL; + uint objects_eval_len; + { + BLI_array_declare(objects_eval); + FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer_eval, v3d, ob_eval) { + BLI_array_append(objects_eval, ob_eval); + } + FOREACH_SELECTED_EDITABLE_OBJECT_END; + objects_eval_len = BLI_array_len(objects_eval); + } + + if (use_transform_skip_children) { + ViewLayer *view_layer = CTX_data_view_layer(C); + + Object **objects = MEM_malloc_arrayN(objects_eval_len, sizeof(*objects), __func__); + + for (int ob_index = 0; ob_index < objects_eval_len; ob_index++) { + Object *ob_eval = objects_eval[ob_index]; + objects[ob_index] = DEG_get_original_object(ob_eval); + } + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + xcs = ED_object_xform_skip_child_container_create(); + ED_object_xform_skip_child_container_item_ensure_from_array( + xcs, view_layer, objects, objects_eval_len); + MEM_freeN(objects); + } if (use_transform_data_origin) { BKE_scene_graph_evaluated_ensure(depsgraph, bmain); xds = ED_object_data_xform_container_create(); } - FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer_eval, v3d, ob_eval) { + for (int ob_index = 0; ob_index < objects_eval_len; ob_index++) { + Object *ob_eval = objects_eval[ob_index]; Object *ob = DEG_get_original_object(ob_eval); vec[0] = -ob_eval->obmat[3][0] + gridf * floorf(0.5f + ob_eval->obmat[3][0] / gridf); vec[1] = -ob_eval->obmat[3][1] + gridf * floorf(0.5f + ob_eval->obmat[3][1] / gridf); @@ -229,8 +261,12 @@ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op)) DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } - FOREACH_SELECTED_EDITABLE_OBJECT_END; + MEM_freeN(objects_eval); + if (use_transform_skip_children) { + ED_object_xform_skip_child_container_update_all(xcs, bmain, depsgraph); + ED_object_xform_skip_child_container_destroy(xcs); + } if (use_transform_data_origin) { ED_object_data_xform_container_update_all(xds, bmain, depsgraph); ED_object_data_xform_container_destroy(xds); @@ -427,41 +463,52 @@ static int snap_selected_to_location(bContext *C, Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - ListBase ctx_data_list; - CollectionPointerLink *ctx_ob; - Object *ob; - - CTX_data_selected_editable_objects(C, &ctx_data_list); - - /* reset flags */ - for (ob = bmain->objects.first; ob; ob = ob->id.next) { + /* Reset flags. */ + for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { ob->flag &= ~OB_DONE; } - /* tag objects we're transforming */ - for (ctx_ob = ctx_data_list.first; ctx_ob; ctx_ob = ctx_ob->next) { - ob = ctx_ob->ptr.data; - ob->flag |= OB_DONE; + /* Build object array, tag objects we're transforming. */ + ViewLayer *view_layer = CTX_data_view_layer(C); + Object **objects = NULL; + uint objects_len; + { + BLI_array_declare(objects); + FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer, v3d, ob) { + BLI_array_append(objects, ob); + ob->flag |= OB_DONE; + } + FOREACH_SELECTED_EDITABLE_OBJECT_END; + objects_len = BLI_array_len(objects); } + const bool use_transform_skip_children = (scene->toolsettings->transform_flag & + SCE_XFORM_SKIP_CHILDREN); const bool use_transform_data_origin = (scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN); + struct XFormObjectSkipChild_Container *xcs = NULL; struct XFormObjectData_Container *xds = NULL; + if (use_transform_skip_children) { + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + xcs = ED_object_xform_skip_child_container_create(); + ED_object_xform_skip_child_container_item_ensure_from_array( + xcs, view_layer, objects, objects_len); + } if (use_transform_data_origin) { BKE_scene_graph_evaluated_ensure(depsgraph, bmain); xds = ED_object_data_xform_container_create(); /* Initialize the transform data in a separate loop because the depsgraph * may be evaluated while setting the locations. */ - for (ctx_ob = ctx_data_list.first; ctx_ob; ctx_ob = ctx_ob->next) { - ob = ctx_ob->ptr.data; + for (int ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; ED_object_data_xform_container_item_ensure(xds, ob); } } - for (ctx_ob = ctx_data_list.first; ctx_ob; ctx_ob = ctx_ob->next) { - ob = ctx_ob->ptr.data; + for (int ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; if ((ob->parent && BKE_object_flag_test_recursive(ob->parent, OB_DONE)) == 0) { @@ -504,9 +551,12 @@ static int snap_selected_to_location(bContext *C, DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } } + MEM_freeN(objects); - BLI_freelistN(&ctx_data_list); - + if (use_transform_skip_children) { + ED_object_xform_skip_child_container_update_all(xcs, bmain, depsgraph); + ED_object_xform_skip_child_container_destroy(xcs); + } if (use_transform_data_origin) { ED_object_data_xform_container_update_all(xds, bmain, depsgraph); ED_object_data_xform_container_destroy(xds); diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 16dfdd35c32..4973b1cb268 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -63,12 +63,10 @@ typedef struct TransDataObject { * - The key is object data #Object. * - The value is #XFormObjectSkipChild. */ - struct GHash *obchild_in_obmode_map; + struct XFormObjectSkipChild_Container *xcs; } TransDataObject; -static void trans_obchild_in_obmode_free_all(TransDataObject *tdo); - static void freeTransObjectCustomData(TransInfo *t, TransDataContainer *UNUSED(tc), TransCustomData *custom_data) @@ -81,7 +79,7 @@ static void freeTransObjectCustomData(TransInfo *t, } if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) { - trans_obchild_in_obmode_free_all(tdo); + ED_object_xform_skip_child_container_destroy(tdo->xcs); } MEM_freeN(tdo); } @@ -117,140 +115,19 @@ void trans_obdata_in_obmode_update_all(TransInfo *t) * Don't transform unselected children, this is done using the parent inverse matrix. * * \note The complex logic here is caused by mixed selection within a single selection chain, - * otherwise we only need #OB_SKIP_CHILD_PARENT_IS_XFORM for single objects. + * otherwise we only need #XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM for single objects. * * \{ */ -enum { - /** - * The parent is transformed, this is held in place. - */ - OB_SKIP_CHILD_PARENT_IS_XFORM = 1, - /** - * The same as #OB_SKIP_CHILD_PARENT_IS_XFORM, - * however this objects parent isn't transformed directly. - */ - OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT = 3, - /** - * Use the parent invert matrix to apply transformation, - * this is needed, because breaks in the selection chain prevents this from being transformed. - * This is used to add the transform which would have been added - * if there weren't breaks in the parent/child chain. - */ - OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM = 2, -}; - -struct XFormObjectSkipChild { - float obmat_orig[4][4]; - float parent_obmat_orig[4][4]; - float parent_obmat_inv_orig[4][4]; - float parent_recurse_obmat_orig[4][4]; - float parentinv_orig[4][4]; - Object *ob_parent_recurse; - int mode; -}; - -static void trans_obchild_in_obmode_ensure_object(TransDataObject *tdo, - Object *ob, - Object *ob_parent_recurse, - int mode) -{ - if (tdo->obchild_in_obmode_map == NULL) { - tdo->obchild_in_obmode_map = BLI_ghash_ptr_new(__func__); - } - - void **xf_p; - if (!BLI_ghash_ensure_p(tdo->obchild_in_obmode_map, ob, &xf_p)) { - struct XFormObjectSkipChild *xf = MEM_mallocN(sizeof(*xf), __func__); - copy_m4_m4(xf->parentinv_orig, ob->parentinv); - copy_m4_m4(xf->obmat_orig, ob->obmat); - copy_m4_m4(xf->parent_obmat_orig, ob->parent->obmat); - invert_m4_m4(xf->parent_obmat_inv_orig, ob->parent->obmat); - if (ob_parent_recurse) { - copy_m4_m4(xf->parent_recurse_obmat_orig, ob_parent_recurse->obmat); - } - xf->mode = mode; - xf->ob_parent_recurse = ob_parent_recurse; - *xf_p = xf; - } -} - void trans_obchild_in_obmode_update_all(TransInfo *t) { TransDataObject *tdo = t->custom.type.data; - if (tdo->obchild_in_obmode_map == NULL) { + if (tdo->xcs == NULL) { return; } struct Main *bmain = CTX_data_main(t->context); - BKE_scene_graph_evaluated_ensure(t->depsgraph, bmain); - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, tdo->obchild_in_obmode_map) { - Object *ob = BLI_ghashIterator_getKey(&gh_iter); - struct XFormObjectSkipChild *xf = BLI_ghashIterator_getValue(&gh_iter); - - /* The following blocks below assign 'dmat'. */ - float dmat[4][4]; - - if (xf->mode == OB_SKIP_CHILD_PARENT_IS_XFORM) { - /* Parent is transformed, this isn't so compensate. */ - Object *ob_parent_eval = DEG_get_evaluated_object(t->depsgraph, ob->parent); - mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, ob_parent_eval->obmat); - invert_m4(dmat); - } - else if (xf->mode == OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT) { - /* Calculate parent matrix (from the root transform). */ - Object *ob_parent_recurse_eval = DEG_get_evaluated_object(t->depsgraph, - xf->ob_parent_recurse); - float parent_recurse_obmat_inv[4][4]; - invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat); - mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv); - invert_m4(dmat); - float parent_obmat_calc[4][4]; - mul_m4_m4m4(parent_obmat_calc, dmat, xf->parent_obmat_orig); - - /* Apply to the parent inverse matrix. */ - mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, parent_obmat_calc); - invert_m4(dmat); - } - else { - BLI_assert(xf->mode == OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM); - /* Transform this - without transform data. */ - Object *ob_parent_recurse_eval = DEG_get_evaluated_object(t->depsgraph, - xf->ob_parent_recurse); - float parent_recurse_obmat_inv[4][4]; - invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->obmat); - mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv); - invert_m4(dmat); - float obmat_calc[4][4]; - mul_m4_m4m4(obmat_calc, dmat, xf->obmat_orig); - /* obmat_calc is just obmat. */ - - /* Get the matrices relative to the parent. */ - float obmat_parent_relative_orig[4][4]; - float obmat_parent_relative_calc[4][4]; - float obmat_parent_relative_inv_orig[4][4]; - - mul_m4_m4m4(obmat_parent_relative_orig, xf->parent_obmat_inv_orig, xf->obmat_orig); - mul_m4_m4m4(obmat_parent_relative_calc, xf->parent_obmat_inv_orig, obmat_calc); - invert_m4_m4(obmat_parent_relative_inv_orig, obmat_parent_relative_orig); - - /* Apply to the parent inverse matrix. */ - mul_m4_m4m4(dmat, obmat_parent_relative_calc, obmat_parent_relative_inv_orig); - } - - mul_m4_m4m4(ob->parentinv, dmat, xf->parentinv_orig); - - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - } -} - -static void trans_obchild_in_obmode_free_all(TransDataObject *tdo) -{ - if (tdo->obchild_in_obmode_map != NULL) { - BLI_ghash_free(tdo->obchild_in_obmode_map, NULL, MEM_freeN); - } + ED_object_xform_skip_child_container_update_all(tdo->xcs, bmain, t->depsgraph); } /** \} */ @@ -746,6 +623,8 @@ void createTransObject(bContext *C, TransInfo *t) if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) { + tdo->xcs = ED_object_xform_skip_child_container_create(); + #define BASE_XFORM_INDIRECT(base) \ ((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0) @@ -778,8 +657,8 @@ void createTransObject(bContext *C, TransInfo *t) } if (ob_parent_recurse) { - trans_obchild_in_obmode_ensure_object( - tdo, ob, ob_parent_recurse, OB_SKIP_CHILD_PARENT_APPLY_TRANSFORM); + ED_object_xform_skip_child_container_item_ensure( + tdo->xcs, ob, ob_parent_recurse, XFORM_OB_SKIP_CHILD_PARENT_APPLY); BLI_ghash_insert(objects_parent_root, ob, ob_parent_recurse); base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE; } @@ -801,14 +680,15 @@ void createTransObject(bContext *C, TransInfo *t) if (base_parent) { if (BASE_XFORM_INDIRECT(base_parent) || BLI_gset_haskey(objects_in_transdata, ob->parent)) { - trans_obchild_in_obmode_ensure_object(tdo, ob, NULL, OB_SKIP_CHILD_PARENT_IS_XFORM); + ED_object_xform_skip_child_container_item_ensure( + tdo->xcs, ob, NULL, XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM); base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE; } else { Object *ob_parent_recurse = BLI_ghash_lookup(objects_parent_root, ob->parent); if (ob_parent_recurse) { - trans_obchild_in_obmode_ensure_object( - tdo, ob, ob_parent_recurse, OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT); + ED_object_xform_skip_child_container_item_ensure( + tdo->xcs, ob, ob_parent_recurse, XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT); } } } -- cgit v1.2.3