tornavis/source/blender/editors/transform/transform_convert_object.c

1019 lines
33 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup edtransform
*/
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_animsys.h"
#include "BKE_context.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_pointcache.h"
#include "BKE_report.h"
#include "BKE_rigidbody.h"
#include "BKE_scene.h"
#include "ED_keyframing.h"
#include "ED_object.h"
#include "DEG_depsgraph_query.h"
#include "transform.h"
#include "transform_orientations.h"
#include "transform_snap.h"
/* Own include. */
#include "transform_convert.h"
/* -------------------------------------------------------------------- */
/** \name Object Mode Custom Data
* \{ */
typedef struct TransDataObject {
/**
* Object to object data transform table.
* Don't add these to transform data because we may want to include child objects
* which aren't being transformed.
*/
struct XFormObjectData_Container *xds;
/**
* Transform
* - The key is object data #Object.
* - The value is #XFormObjectSkipChild.
*/
struct XFormObjectSkipChild_Container *xcs;
} TransDataObject;
static void freeTransObjectCustomData(TransInfo *t,
TransDataContainer *UNUSED(tc),
TransCustomData *custom_data)
{
TransDataObject *tdo = custom_data->data;
custom_data->data = NULL;
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
ED_object_data_xform_container_destroy(tdo->xds);
}
if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) {
ED_object_xform_skip_child_container_destroy(tdo->xcs);
}
MEM_freeN(tdo);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Object Data in Object Mode
*
* Use to implement 'Affect Only Origins' feature.
* We need this to be detached from transform data because,
* unlike transforming regular objects, we need to transform the children.
*
* Nearly all of the logic here is in the 'ED_object_data_xform_container_*' API.
* \{ */
static void trans_obdata_in_obmode_update_all(TransInfo *t)
{
TransDataObject *tdo = t->custom.type.data;
if (tdo->xds == NULL) {
return;
}
struct Main *bmain = CTX_data_main(t->context);
ED_object_data_xform_container_update_all(tdo->xds, bmain, t->depsgraph);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \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.
*
* \{ */
static void trans_obchild_in_obmode_update_all(TransInfo *t)
{
TransDataObject *tdo = t->custom.type.data;
if (tdo->xcs == NULL) {
return;
}
struct Main *bmain = CTX_data_main(t->context);
ED_object_xform_skip_child_container_update_all(tdo->xcs, bmain, t->depsgraph);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Object Transform Creation
*
* Instead of transforming the selection, move the 2D/3D cursor.
*
* \{ */
/* *********************** Object Transform data ******************* */
/* transcribe given object into TransData for Transforming */
static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
{
Scene *scene = t->scene;
bool constinv;
bool skip_invert = false;
if (t->mode != TFM_DUMMY && ob->rigidbody_object) {
float rot[3][3], scale[3];
float ctime = BKE_scene_ctime_get(scene);
/* only use rigid body transform if simulation is running,
* avoids problems with initial setup of rigid bodies */
if (BKE_rigidbody_check_sim_running(scene->rigidbody_world, ctime)) {
/* save original object transform */
copy_v3_v3(td->ext->oloc, ob->loc);
if (ob->rotmode > 0) {
copy_v3_v3(td->ext->orot, ob->rot);
}
else if (ob->rotmode == ROT_MODE_AXISANGLE) {
td->ext->orotAngle = ob->rotAngle;
copy_v3_v3(td->ext->orotAxis, ob->rotAxis);
}
else {
copy_qt_qt(td->ext->oquat, ob->quat);
}
/* update object's loc/rot to get current rigid body transform */
mat4_to_loc_rot_size(ob->loc, rot, scale, ob->object_to_world);
sub_v3_v3(ob->loc, ob->dloc);
BKE_object_mat3_to_rot(ob, rot, false); /* drot is already corrected here */
}
}
/* axismtx has the real orientation */
transform_orientations_create_from_axis(td->axismtx, UNPACK3(ob->object_to_world));
if (t->orient_type_mask & (1 << V3D_ORIENT_GIMBAL)) {
if (!gimbal_axis_object(ob, td->ext->axismtx_gimbal)) {
copy_m3_m3(td->ext->axismtx_gimbal, td->axismtx);
}
}
td->con = ob->constraints.first;
/* hack: temporarily disable tracking and/or constraints when getting
* object matrix, if tracking is on, or if constraints don't need
* inverse correction to stop it from screwing up space conversion
* matrix later
*/
constinv = constraints_list_needinv(t, &ob->constraints);
/* disable constraints inversion for dummy pass */
if (t->mode == TFM_DUMMY) {
skip_invert = true;
}
/* NOTE: This is not really following copy-on-write design and we should not
* be re-evaluating the evaluated object. But as the comment above mentioned
* this is part of a hack.
* More proper solution would be to make a shallow copy of the object and
* evaluate that, and access matrix of that evaluated copy of the object.
* Might be more tricky than it sounds, if some logic later on accesses the
* object matrix via td->ob->object_to_world. */
Object *object_eval = DEG_get_evaluated_object(t->depsgraph, ob);
if (skip_invert == false && constinv == false) {
object_eval->transflag |= OB_NO_CONSTRAINTS; /* BKE_object_where_is_calc checks this */
/* It is possible to have transform data initialization prior to a
* complete dependency graph evaluated. Happens, for example, when
* changing transformation mode. */
BKE_object_tfm_copy(object_eval, ob);
BKE_object_where_is_calc(t->depsgraph, t->scene, object_eval);
object_eval->transflag &= ~OB_NO_CONSTRAINTS;
}
else {
BKE_object_where_is_calc(t->depsgraph, t->scene, object_eval);
}
/* Copy newly evaluated fields to the original object, similar to how
* active dependency graph will do it. */
copy_m4_m4(ob->object_to_world, object_eval->object_to_world);
/* Only copy negative scale flag, this is the only flag which is modified by
* the BKE_object_where_is_calc(). The rest of the flags we need to keep,
* otherwise we might lose dupli flags (see #61787). */
ob->transflag &= ~OB_NEG_SCALE;
ob->transflag |= (object_eval->transflag & OB_NEG_SCALE);
td->ob = ob;
td->loc = ob->loc;
copy_v3_v3(td->iloc, td->loc);
if (ob->rotmode > 0) {
td->ext->rot = ob->rot;
td->ext->rotAxis = NULL;
td->ext->rotAngle = NULL;
td->ext->quat = NULL;
copy_v3_v3(td->ext->irot, ob->rot);
copy_v3_v3(td->ext->drot, ob->drot);
}
else if (ob->rotmode == ROT_MODE_AXISANGLE) {
td->ext->rot = NULL;
td->ext->rotAxis = ob->rotAxis;
td->ext->rotAngle = &ob->rotAngle;
td->ext->quat = NULL;
td->ext->irotAngle = ob->rotAngle;
copy_v3_v3(td->ext->irotAxis, ob->rotAxis);
/* XXX, not implemented. */
#if 0
td->ext->drotAngle = ob->drotAngle;
copy_v3_v3(td->ext->drotAxis, ob->drotAxis);
#endif
}
else {
td->ext->rot = NULL;
td->ext->rotAxis = NULL;
td->ext->rotAngle = NULL;
td->ext->quat = ob->quat;
copy_qt_qt(td->ext->iquat, ob->quat);
copy_qt_qt(td->ext->dquat, ob->dquat);
}
td->ext->rotOrder = ob->rotmode;
td->ext->size = ob->scale;
copy_v3_v3(td->ext->isize, ob->scale);
copy_v3_v3(td->ext->dscale, ob->dscale);
copy_v3_v3(td->center, ob->object_to_world[3]);
copy_m4_m4(td->ext->obmat, ob->object_to_world);
/* is there a need to set the global<->data space conversion matrices? */
if (ob->parent || constinv) {
float obmtx[3][3], totmat[3][3], obinv[3][3];
/* Get the effect of parenting, and/or certain constraints.
* NOTE: some Constraints, and also Tracking should never get this
* done, as it doesn't work well.
*/
BKE_object_to_mat3(ob, obmtx);
copy_m3_m4(totmat, ob->object_to_world);
/* If the object scale is zero on any axis, this might result in a zero matrix.
* In this case, the transformation would not do anything, see: #50103. */
orthogonalize_m3_zero_axes(obmtx, 1.0f);
orthogonalize_m3_zero_axes(totmat, 1.0f);
/* Use safe invert even though the input matrices have had zero axes set to unit length,
* in the unlikely case of failure (float precision for eg) this uses unit matrix fallback. */
invert_m3_m3_safe_ortho(obinv, totmat);
mul_m3_m3m3(td->smtx, obmtx, obinv);
invert_m3_m3_safe_ortho(td->mtx, td->smtx);
}
else {
/* No conversion to/from data-space. */
unit_m3(td->smtx);
unit_m3(td->mtx);
}
}
static void trans_object_base_deps_flag_prepare(const Scene *scene, ViewLayer *view_layer)
{
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
base->object->id.tag &= ~LIB_TAG_DOIT;
}
}
static void set_trans_object_base_deps_flag_cb(ID *id,
eDepsObjectComponentType component,
void *UNUSED(user_data))
{
/* Here we only handle object IDs. */
if (GS(id->name) != ID_OB) {
return;
}
if (!ELEM(component, DEG_OB_COMP_TRANSFORM, DEG_OB_COMP_GEOMETRY)) {
return;
}
id->tag |= LIB_TAG_DOIT;
}
static void flush_trans_object_base_deps_flag(Depsgraph *depsgraph, Object *object)
{
object->id.tag |= LIB_TAG_DOIT;
DEG_foreach_dependent_ID_component(depsgraph,
&object->id,
DEG_OB_COMP_TRANSFORM,
DEG_FOREACH_COMPONENT_IGNORE_TRANSFORM_SOLVERS,
set_trans_object_base_deps_flag_cb,
NULL);
}
static void trans_object_base_deps_flag_finish(const TransInfo *t,
const Scene *scene,
ViewLayer *view_layer)
{
if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) {
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->object->id.tag & LIB_TAG_DOIT) {
base->flag_legacy |= BA_SNAP_FIX_DEPS_FIASCO;
}
}
}
}
/* sets flags in Bases to define whether they take part in transform */
/* it deselects Bases, so we have to call the clear function always after */
static void set_trans_object_base_flags(TransInfo *t)
{
Main *bmain = CTX_data_main(t->context);
ViewLayer *view_layer = t->view_layer;
View3D *v3d = t->view;
Scene *scene = t->scene;
Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
/* NOTE: if Base selected and has parent selected:
* base->flag_legacy = BA_WAS_SEL
*/
/* Don't do it if we're not actually going to recalculate anything. */
if (t->mode == TFM_DUMMY) {
return;
}
/* Makes sure base flags and object flags are identical. */
BKE_scene_base_flag_to_objects(t->scene, t->view_layer);
/* Make sure depsgraph is here. */
DEG_graph_relations_update(depsgraph);
/* Clear all flags we need. It will be used to detect dependencies. */
trans_object_base_deps_flag_prepare(scene, view_layer);
/* Traverse all bases and set all possible flags. */
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
base->flag_legacy &= ~(BA_WAS_SEL | BA_TRANSFORM_LOCKED_IN_PLACE);
if (BASE_SELECTED_EDITABLE(v3d, base)) {
Object *ob = base->object;
Object *parsel = ob->parent;
/* If parent selected, deselect. */
while (parsel != NULL) {
if (parsel->base_flag & BASE_SELECTED) {
Base *parbase = BKE_view_layer_base_find(view_layer, parsel);
if (parbase != NULL) { /* in rare cases this can fail */
if (BASE_SELECTED_EDITABLE(v3d, parbase)) {
break;
}
}
}
parsel = parsel->parent;
}
if (parsel != NULL) {
/* Rotation around local centers are allowed to propagate. */
if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && ELEM(t->mode, TFM_ROTATION, TFM_TRACKBALL))
{
base->flag_legacy |= BA_TRANSFORM_CHILD;
}
else {
base->flag &= ~BASE_SELECTED;
base->flag_legacy |= BA_WAS_SEL;
}
}
flush_trans_object_base_deps_flag(depsgraph, ob);
}
}
/* Store temporary bits in base indicating that base is being modified
* (directly or indirectly) by transforming objects.
*/
trans_object_base_deps_flag_finish(t, scene, view_layer);
}
static bool mark_children(Object *ob)
{
if (ob->flag & (SELECT | BA_TRANSFORM_CHILD)) {
return true;
}
if (ob->parent) {
if (mark_children(ob->parent)) {
ob->flag |= BA_TRANSFORM_CHILD;
return true;
}
}
return false;
}
static int count_proportional_objects(TransInfo *t)
{
int total = 0;
ViewLayer *view_layer = t->view_layer;
View3D *v3d = t->view;
struct Main *bmain = CTX_data_main(t->context);
Scene *scene = t->scene;
Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
/* Clear all flags we need. It will be used to detect dependencies. */
trans_object_base_deps_flag_prepare(scene, view_layer);
/* Rotations around local centers are allowed to propagate, so we take all objects. */
if (!((t->around == V3D_AROUND_LOCAL_ORIGINS) && ELEM(t->mode, TFM_ROTATION, TFM_TRACKBALL))) {
/* Mark all parents. */
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_SELECTED_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)) {
Object *parent = base->object->parent;
/* flag all parents */
while (parent != NULL) {
parent->flag |= BA_TRANSFORM_PARENT;
parent = parent->parent;
}
}
}
/* Mark all children. */
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
/* all base not already selected or marked that is editable */
if ((base->object->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 &&
(base->flag & BASE_SELECTED) == 0 &&
(BASE_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)))
{
mark_children(base->object);
}
}
}
/* Flush changed flags to all dependencies. */
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
/* If base is not selected, not a parent of selection or not a child of
* selection and it is editable and selectable.
*/
if ((ob->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 &&
(base->flag & BASE_SELECTED) == 0 &&
(BASE_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)))
{
flush_trans_object_base_deps_flag(depsgraph, ob);
total += 1;
}
}
/* Store temporary bits in base indicating that base is being modified
* (directly or indirectly) by transforming objects.
*/
trans_object_base_deps_flag_finish(t, scene, view_layer);
return total;
}
static void clear_trans_object_base_flags(TransInfo *t)
{
Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->flag_legacy & BA_WAS_SEL) {
ED_object_base_select(base, BA_SELECT);
}
base->flag_legacy &= ~(BA_WAS_SEL | BA_SNAP_FIX_DEPS_FIASCO | BA_TEMP_TAG |
BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT |
BA_TRANSFORM_LOCKED_IN_PLACE);
}
}
static void createTransObject(bContext *C, TransInfo *t)
{
Main *bmain = CTX_data_main(C);
TransData *td = NULL;
TransDataExtension *tx;
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
set_trans_object_base_flags(t);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* count */
tc->data_len = CTX_DATA_COUNT(C, selected_bases);
if (!tc->data_len) {
/* clear here, main transform function escapes too */
clear_trans_object_base_flags(t);
return;
}
if (is_prop_edit) {
tc->data_len += count_proportional_objects(t);
}
td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransOb");
tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransObExtension");
TransDataObject *tdo = MEM_callocN(sizeof(*tdo), __func__);
t->custom.type.data = tdo;
t->custom.type.free_cb = freeTransObjectCustomData;
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
tdo->xds = ED_object_data_xform_container_create();
}
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
Object *ob = base->object;
td->flag = TD_SELECTED;
td->protectflag = ob->protectflag;
td->ext = tx;
td->ext->rotOrder = ob->rotmode;
if (base->flag & BA_TRANSFORM_CHILD) {
td->flag |= TD_NOCENTER;
td->flag |= TD_NO_LOC;
}
/* select linked objects, but skip them later */
if (!BKE_id_is_editable(bmain, &ob->id)) {
td->flag |= TD_SKIP;
}
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
ID *id = ob->data;
if (!id || id->lib) {
td->flag |= TD_SKIP;
}
else if (BKE_object_is_in_editmode(ob)) {
/* The object could have edit-mode data from another view-layer,
* it's such a corner-case it can be skipped for now - Campbell. */
td->flag |= TD_SKIP;
}
}
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
if ((td->flag & TD_SKIP) == 0) {
ED_object_data_xform_container_item_ensure(tdo->xds, ob);
}
}
ObjectToTransData(t, td, ob);
td->val = NULL;
td++;
tx++;
}
CTX_DATA_END;
if (is_prop_edit) {
Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
View3D *v3d = t->view;
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
/* if base is not selected, not a parent of selection
* or not a child of selection and it is editable and selectable */
if ((ob->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 &&
(base->flag & BASE_SELECTED) == 0 && BASE_EDITABLE(v3d, base) &&
BASE_SELECTABLE(v3d, base))
{
td->protectflag = ob->protectflag;
td->ext = tx;
td->ext->rotOrder = ob->rotmode;
ObjectToTransData(t, td, ob);
td->val = NULL;
td++;
tx++;
}
}
}
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
GSet *objects_in_transdata = BLI_gset_ptr_new_ex(__func__, tc->data_len);
td = tc->data;
for (int i = 0; i < tc->data_len; i++, td++) {
if ((td->flag & TD_SKIP) == 0) {
BLI_gset_add(objects_in_transdata, td->ob);
}
}
Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
View3D *v3d = t->view;
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
/* if base is not selected, not a parent of selection
* or not a child of selection and it is editable and selectable */
if ((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0 &&
BASE_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base))
{
Object *ob_parent = ob->parent;
if (ob_parent != NULL) {
if (!BLI_gset_haskey(objects_in_transdata, ob)) {
bool parent_in_transdata = false;
while (ob_parent != NULL) {
if (BLI_gset_haskey(objects_in_transdata, ob_parent)) {
parent_in_transdata = true;
break;
}
ob_parent = ob_parent->parent;
}
if (parent_in_transdata) {
ED_object_data_xform_container_item_ensure(tdo->xds, ob);
}
}
}
}
}
BLI_gset_free(objects_in_transdata, NULL);
}
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)
GSet *objects_in_transdata = BLI_gset_ptr_new_ex(__func__, tc->data_len);
GHash *objects_parent_root = BLI_ghash_ptr_new_ex(__func__, tc->data_len);
td = tc->data;
for (int i = 0; i < tc->data_len; i++, td++) {
if ((td->flag & TD_SKIP) == 0) {
BLI_gset_add(objects_in_transdata, td->ob);
}
}
Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
if (ob->parent != NULL) {
if (ob->parent && !BLI_gset_haskey(objects_in_transdata, ob->parent) &&
!BLI_gset_haskey(objects_in_transdata, ob))
{
if ((base->flag_legacy & BA_WAS_SEL) && (base->flag & BASE_SELECTED) == 0) {
Base *base_parent = BKE_view_layer_base_find(view_layer, ob->parent);
if (base_parent && !BASE_XFORM_INDIRECT(base_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(
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;
}
}
}
}
}
}
}
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
if (BASE_XFORM_INDIRECT(base) || BLI_gset_haskey(objects_in_transdata, ob)) {
/* pass. */
}
else if (ob->parent != NULL) {
Base *base_parent = BKE_view_layer_base_find(view_layer, ob->parent);
if (base_parent) {
if (BASE_XFORM_INDIRECT(base_parent) ||
BLI_gset_haskey(objects_in_transdata, ob->parent)) {
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) {
ED_object_xform_skip_child_container_item_ensure(
tdo->xcs, ob, ob_parent_recurse, XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT);
}
}
}
}
}
BLI_gset_free(objects_in_transdata, NULL);
BLI_ghash_free(objects_parent_root, NULL, NULL);
#undef BASE_XFORM_INDIRECT
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Transform (Auto-Keyframing)
* \{ */
/**
* Auto-keyframing feature - for objects
*
* \param tmode: A transform mode.
*
* \note Context may not always be available,
* so must check before using it as it's a luxury for a few cases.
*/
static void autokeyframe_object(
bContext *C, Scene *scene, ViewLayer *view_layer, Object *ob, int tmode)
{
Main *bmain = CTX_data_main(C);
ID *id = &ob->id;
FCurve *fcu;
/* TODO: this should probably be done per channel instead. */
if (autokeyframe_cfra_can_key(scene, id)) {
ReportList *reports = CTX_wm_reports(C);
ToolSettings *ts = scene->toolsettings;
KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
ListBase dsources = {NULL, NULL};
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
depsgraph, (float)scene->r.cfra);
eInsertKeyFlags flag = 0;
/* Get flags used for inserting keyframes. */
flag = ANIM_get_keyframing_flags(scene, true);
/* Add data-source override for the object. */
ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL);
if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
/* Only insert into active keyingset
* NOTE: we assume here that the active Keying Set
* does not need to have its iterator overridden.
*/
ANIM_apply_keyingset(
C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) {
AnimData *adt = ob->adt;
/* only key on available channels */
if (adt && adt->action) {
ListBase nla_cache = {NULL, NULL};
for (fcu = adt->action->curves.first; fcu; fcu = fcu->next) {
insert_keyframe(bmain,
reports,
id,
adt->action,
(fcu->grp ? fcu->grp->name : NULL),
fcu->rna_path,
fcu->array_index,
&anim_eval_context,
ts->keyframe_type,
&nla_cache,
flag);
}
BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
}
}
else if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) {
bool do_loc = false, do_rot = false, do_scale = false;
/* filter the conditions when this happens (assume that curarea->spacetype==SPACE_VIE3D) */
if (tmode == TFM_TRANSLATION) {
do_loc = true;
}
else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) {
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
BKE_view_layer_synced_ensure(scene, view_layer);
if (ob != BKE_view_layer_active_object_get(view_layer)) {
do_loc = true;
}
}
else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) {
do_loc = true;
}
if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
do_rot = true;
}
}
else if (tmode == TFM_RESIZE) {
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
BKE_view_layer_synced_ensure(scene, view_layer);
if (ob != BKE_view_layer_active_object_get(view_layer)) {
do_loc = true;
}
}
else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) {
do_loc = true;
}
if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
do_scale = true;
}
}
/* insert keyframes for the affected sets of channels using the builtin KeyingSets found */
if (do_loc) {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID);
ANIM_apply_keyingset(
C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
if (do_rot) {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID);
ANIM_apply_keyingset(
C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
if (do_scale) {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_SCALING_ID);
ANIM_apply_keyingset(
C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
}
/* insert keyframe in all (transform) channels */
else {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
ANIM_apply_keyingset(
C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
/* free temp info */
BLI_freelistN(&dsources);
}
}
/* Return if we need to update motion paths, only if they already exist,
* and we will insert a keyframe at the end of transform. */
static bool motionpath_need_update_object(Scene *scene, Object *ob)
{
/* XXX: there's potential here for problems with unkeyed rotations/scale,
* but for now (until proper data-locality for baking operations),
* this should be a better fix for #24451 and #37755
*/
if (autokeyframe_cfra_can_key(scene, &ob->id)) {
return (ob->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0;
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Recalc Data object
* \{ */
static void recalcData_objects(TransInfo *t)
{
bool motionpath_update = false;
if (t->state != TRANS_CANCEL) {
transform_snap_project_individual_apply(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
for (int i = 0; i < tc->data_len; i++, td++) {
Object *ob = td->ob;
if (td->flag & TD_SKIP) {
continue;
}
/* if animtimer is running, and the object already has animation data,
* check if the auto-record feature means that we should record 'samples'
* (i.e. uneditable animation values)
*/
/* TODO: autokeyframe calls need some setting to specify to add samples
* (FPoints) instead of keyframes? */
if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) {
animrecord_check_state(t, &ob->id);
autokeyframe_object(t->context, t->scene, t->view_layer, ob, t->mode);
}
motionpath_update |= motionpath_need_update_object(t->scene, ob);
/* sets recalc flags fully, instead of flushing existing ones
* otherwise proxies don't function correctly
*/
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
}
}
if (motionpath_update) {
/* Update motion paths once for all transformed objects. */
ED_objects_recalculate_paths_selected(
t->context, t->scene, OBJECT_PATH_CALC_RANGE_CURRENT_FRAME);
}
if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) {
trans_obchild_in_obmode_update_all(t);
}
if (t->options & CTX_OBMODE_XFORM_OBDATA) {
trans_obdata_in_obmode_update_all(t);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Special After Transform Object
* \{ */
static void special_aftertrans_update__object(bContext *C, TransInfo *t)
{
BLI_assert(t->options & CTX_OBJECT);
Object *ob;
const bool canceled = (t->state == TRANS_CANCEL);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
bool motionpath_update = false;
for (int i = 0; i < tc->data_len; i++) {
TransData *td = tc->data + i;
ListBase pidlist;
PTCacheID *pid;
ob = td->ob;
if (td->flag & TD_SKIP) {
continue;
}
/* flag object caches as outdated */
BKE_ptcache_ids_from_object(&pidlist, ob, t->scene, MAX_DUPLI_RECUR);
for (pid = pidlist.first; pid; pid = pid->next) {
if (pid->type != PTCACHE_TYPE_PARTICLES) {
/* particles don't need reset on geometry change */
pid->cache->flag |= PTCACHE_OUTDATED;
}
}
BLI_freelistN(&pidlist);
/* Point-cache refresh. */
if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
/* Needed for proper updating of "quick cached" dynamics.
* Creates troubles for moving animated objects without
* auto-key though, probably needed is an animation-system override?
* NOTE(@jahka): Please remove if some other solution is found. */
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
/* Set auto-key if necessary. */
if (!canceled) {
autokeyframe_object(C, t->scene, t->view_layer, ob, t->mode);
}
motionpath_update |= motionpath_need_update_object(t->scene, ob);
/* Restore rigid body transform. */
if (ob->rigidbody_object && canceled) {
float ctime = BKE_scene_ctime_get(t->scene);
if (BKE_rigidbody_check_sim_running(t->scene->rigidbody_world, ctime)) {
BKE_rigidbody_aftertrans_update(ob,
td->ext->oloc,
td->ext->orot,
td->ext->oquat,
td->ext->orotAxis,
td->ext->orotAngle);
}
}
}
if (motionpath_update) {
/* Update motion paths once for all transformed objects. */
const eObjectPathCalcRange range = canceled ? OBJECT_PATH_CALC_RANGE_CURRENT_FRAME :
OBJECT_PATH_CALC_RANGE_CHANGED;
ED_objects_recalculate_paths_selected(C, t->scene, range);
}
clear_trans_object_base_flags(t);
}
/** \} */
TransConvertTypeInfo TransConvertType_Object = {
/*flags*/ 0,
/*createTransData*/ createTransObject,
/*recalcData*/ recalcData_objects,
/*special_aftertrans_update*/ special_aftertrans_update__object,
};