From e7e3431b299448310f9c9e95d863055e3e04a8c4 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 4 Nov 2021 16:51:37 -0300 Subject: Cleanup: Move object.c to C++ This is useful to allow the use of features made in C++. Differential Revision: https://developer.blender.org/D13115 --- source/blender/blenkernel/BKE_lib_id.h | 3 + source/blender/blenkernel/BKE_object.h | 6 +- source/blender/blenkernel/CMakeLists.txt | 2 +- source/blender/blenkernel/intern/object.c | 5868 ---------------------- source/blender/blenkernel/intern/object.cc | 5851 +++++++++++++++++++++ source/blender/makesdna/DNA_object_force_types.h | 2 + 6 files changed, 5860 insertions(+), 5872 deletions(-) delete mode 100644 source/blender/blenkernel/intern/object.c create mode 100644 source/blender/blenkernel/intern/object.cc diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index d79df4b2216..402787c8cc0 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -46,6 +46,7 @@ */ #include "BLI_compiler_attrs.h" +#include "BLI_utildefines.h" #ifdef __cplusplus extern "C" { @@ -179,6 +180,8 @@ typedef enum eLibIDDuplicateFlags { LIB_ID_DUPLICATE_IS_ROOT_ID = 1 << 1, } eLibIDDuplicateFlags; +ENUM_OPERATORS(eLibIDDuplicateFlags, LIB_ID_DUPLICATE_IS_ROOT_ID) + /* lib_remap.c (keep here since they're general functions) */ /** * New freeing logic options. diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 0e153c5a82a..4e53af5562f 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -75,7 +75,7 @@ void BKE_object_modifier_gpencil_hook_reset(struct Object *ob, struct HookGpencilModifierData *hmd); bool BKE_object_modifier_gpencil_use_time(struct Object *ob, struct GpencilModifierData *md); -bool BKE_object_shaderfx_use_time(struct Object *ob, struct ShaderFxData *md); +bool BKE_object_shaderfx_use_time(struct Object *ob, struct ShaderFxData *fx); bool BKE_object_supports_modifiers(const struct Object *ob); bool BKE_object_support_modifier_type_check(const struct Object *ob, int modifier_type); @@ -89,7 +89,7 @@ bool BKE_object_copy_modifier(struct Main *bmain, struct Object *ob_dst, const struct Object *ob_src, struct ModifierData *md); -bool BKE_object_copy_gpencil_modifier(struct Object *ob_dst, struct GpencilModifierData *md); +bool BKE_object_copy_gpencil_modifier(struct Object *ob_dst, struct GpencilModifierData *gmd_src); bool BKE_object_modifier_stack_copy(struct Object *ob_dst, const struct Object *ob_src, const bool do_copy_all, @@ -155,7 +155,7 @@ bool BKE_object_obdata_is_libdata(const struct Object *ob); struct Object *BKE_object_duplicate(struct Main *bmain, struct Object *ob, uint dupflag, - const uint duplicate_options); + uint duplicate_options); void BKE_object_obdata_size_init(struct Object *ob, const float size); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index ef95d747948..d4ec7fd703d 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -226,7 +226,7 @@ set(SRC intern/multires_versioning.c intern/nla.c intern/node.cc - intern/object.c + intern/object.cc intern/object_deform.c intern/object_dupli.cc intern/object_facemap.c diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c deleted file mode 100644 index dc6ef580408..00000000000 --- a/source/blender/blenkernel/intern/object.c +++ /dev/null @@ -1,5868 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup bke - */ - -/* Allow using deprecated functionality for .blend file I/O. */ -#define DNA_DEPRECATED_ALLOW - -#include -#include -#include - -#include "CLG_log.h" - -#include "MEM_guardedalloc.h" - -#include "DNA_anim_types.h" -#include "DNA_armature_types.h" -#include "DNA_camera_types.h" -#include "DNA_collection_types.h" -#include "DNA_constraint_types.h" -#include "DNA_defaults.h" -#include "DNA_dynamicpaint_types.h" -#include "DNA_effect_types.h" -#include "DNA_fluid_types.h" -#include "DNA_gpencil_modifier_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_key_types.h" -#include "DNA_lattice_types.h" -#include "DNA_light_types.h" -#include "DNA_lightprobe_types.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_meta_types.h" -#include "DNA_movieclip_types.h" -#include "DNA_nla_types.h" -#include "DNA_object_fluidsim_types.h" -#include "DNA_object_types.h" -#include "DNA_pointcloud_types.h" -#include "DNA_rigidbody_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_sequence_types.h" -#include "DNA_shader_fx_types.h" -#include "DNA_space_types.h" -#include "DNA_view3d_types.h" -#include "DNA_world_types.h" - -#include "BLI_blenlib.h" -#include "BLI_kdtree.h" -#include "BLI_linklist.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_threads.h" -#include "BLI_utildefines.h" - -#include "BLT_translation.h" - -#include "BKE_DerivedMesh.h" -#include "BKE_action.h" -#include "BKE_anim_data.h" -#include "BKE_anim_path.h" -#include "BKE_anim_visualization.h" -#include "BKE_animsys.h" -#include "BKE_armature.h" -#include "BKE_asset.h" -#include "BKE_camera.h" -#include "BKE_collection.h" -#include "BKE_constraint.h" -#include "BKE_curve.h" -#include "BKE_deform.h" -#include "BKE_displist.h" -#include "BKE_duplilist.h" -#include "BKE_editmesh.h" -#include "BKE_editmesh_cache.h" -#include "BKE_effect.h" -#include "BKE_fcurve.h" -#include "BKE_fcurve_driver.h" -#include "BKE_geometry_set.h" -#include "BKE_global.h" -#include "BKE_gpencil.h" -#include "BKE_gpencil_geom.h" -#include "BKE_gpencil_modifier.h" -#include "BKE_hair.h" -#include "BKE_icons.h" -#include "BKE_idprop.h" -#include "BKE_idtype.h" -#include "BKE_image.h" -#include "BKE_key.h" -#include "BKE_lattice.h" -#include "BKE_layer.h" -#include "BKE_lib_id.h" -#include "BKE_lib_query.h" -#include "BKE_lib_remap.h" -#include "BKE_light.h" -#include "BKE_lightprobe.h" -#include "BKE_linestyle.h" -#include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_mball.h" -#include "BKE_mesh.h" -#include "BKE_mesh_wrapper.h" -#include "BKE_modifier.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" -#include "BKE_object_facemap.h" -#include "BKE_paint.h" -#include "BKE_particle.h" -#include "BKE_pbvh.h" -#include "BKE_pointcache.h" -#include "BKE_pointcloud.h" -#include "BKE_rigidbody.h" -#include "BKE_scene.h" -#include "BKE_shader_fx.h" -#include "BKE_softbody.h" -#include "BKE_speaker.h" -#include "BKE_subdiv_ccg.h" -#include "BKE_subsurf.h" -#include "BKE_vfont.h" -#include "BKE_volume.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "DRW_engine.h" - -#include "BLO_read_write.h" -#include "BLO_readfile.h" - -#include "SEQ_sequencer.h" - -#ifdef WITH_PYTHON -# include "BPY_extern.h" -#endif - -#include "CCGSubSurf.h" -#include "atomic_ops.h" - -static CLG_LogRef LOG = {"bke.object"}; - -/** - * Vertex parent modifies original BMesh which is not safe for threading. - * Ideally such a modification should be handled as a separate DAG update - * callback for mesh datablock, but for until it is actually supported use - * simpler solution with a mutex lock. - * - sergey - - */ -#define VPARENT_THREADING_HACK - -#ifdef VPARENT_THREADING_HACK -static ThreadMutex vparent_lock = BLI_MUTEX_INITIALIZER; -#endif - -static void copy_object_pose(Object *obn, const Object *ob, const int flag); - -static void object_init_data(ID *id) -{ - Object *ob = (Object *)id; - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(ob, id)); - - MEMCPY_STRUCT_AFTER(ob, DNA_struct_default_get(Object), id); - - ob->type = OB_EMPTY; - - ob->trackflag = OB_POSY; - ob->upflag = OB_POSZ; - - /* Animation Visualization defaults */ - animviz_settings_init(&ob->avs); -} - -static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int flag) -{ - Object *ob_dst = (Object *)id_dst; - const Object *ob_src = (const Object *)id_src; - - /* Do not copy runtime data. */ - BKE_object_runtime_reset_on_copy(ob_dst, flag); - - /* We never handle usercount here for own data. */ - const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; - - if (ob_src->totcol) { - ob_dst->mat = MEM_dupallocN(ob_src->mat); - ob_dst->matbits = MEM_dupallocN(ob_src->matbits); - ob_dst->totcol = ob_src->totcol; - } - else if (ob_dst->mat != NULL || ob_dst->matbits != NULL) { - /* This shall not be needed, but better be safe than sorry. */ - BLI_assert_msg( - 0, "Object copy: non-NULL material pointers with zero counter, should not happen."); - ob_dst->mat = NULL; - ob_dst->matbits = NULL; - } - - if (ob_src->iuser) { - ob_dst->iuser = MEM_dupallocN(ob_src->iuser); - } - - if (ob_src->runtime.bb) { - ob_dst->runtime.bb = MEM_dupallocN(ob_src->runtime.bb); - } - - BLI_listbase_clear(&ob_dst->shader_fx); - LISTBASE_FOREACH (ShaderFxData *, fx, &ob_src->shader_fx) { - ShaderFxData *nfx = BKE_shaderfx_new(fx->type); - BLI_strncpy(nfx->name, fx->name, sizeof(nfx->name)); - BKE_shaderfx_copydata_ex(fx, nfx, flag_subdata); - BLI_addtail(&ob_dst->shader_fx, nfx); - } - - if (ob_src->pose) { - copy_object_pose(ob_dst, ob_src, flag_subdata); - /* backwards compat... non-armatures can get poses in older files? */ - if (ob_src->type == OB_ARMATURE) { - const bool do_pose_id_user = (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0; - BKE_pose_rebuild(bmain, ob_dst, ob_dst->data, do_pose_id_user); - } - } - - BKE_object_facemap_copy_list(&ob_dst->fmaps, &ob_src->fmaps); - BKE_constraints_copy_ex(&ob_dst->constraints, &ob_src->constraints, flag_subdata, true); - - ob_dst->mode = ob_dst->type != OB_GPENCIL ? OB_MODE_OBJECT : ob_dst->mode; - ob_dst->sculpt = NULL; - - if (ob_src->pd) { - ob_dst->pd = MEM_dupallocN(ob_src->pd); - if (ob_dst->pd->rng) { - ob_dst->pd->rng = MEM_dupallocN(ob_src->pd->rng); - } - } - BKE_rigidbody_object_copy(bmain, ob_dst, ob_src, flag_subdata); - - BLI_listbase_clear(&ob_dst->modifiers); - BLI_listbase_clear(&ob_dst->greasepencil_modifiers); - /* NOTE: Also takes care of softbody and particle systems copying. */ - BKE_object_modifier_stack_copy(ob_dst, ob_src, true, flag_subdata); - - BLI_listbase_clear((ListBase *)&ob_dst->drawdata); - BLI_listbase_clear(&ob_dst->pc_ids); - - ob_dst->avs = ob_src->avs; - ob_dst->mpath = animviz_copy_motionpath(ob_src->mpath); - - /* Do not copy object's preview - * (mostly due to the fact renderers create temp copy of objects). */ - if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0 && false) { /* XXX TODO: temp hack. */ - BKE_previewimg_id_copy(&ob_dst->id, &ob_src->id); - } - else { - ob_dst->preview = NULL; - } -} - -static void object_free_data(ID *id) -{ - Object *ob = (Object *)id; - - DRW_drawdata_free((ID *)ob); - - /* BKE__free shall never touch to ID->us. Never ever. */ - BKE_object_free_modifiers(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); - BKE_object_free_shaderfx(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); - - MEM_SAFE_FREE(ob->mat); - MEM_SAFE_FREE(ob->matbits); - MEM_SAFE_FREE(ob->iuser); - MEM_SAFE_FREE(ob->runtime.bb); - - BLI_freelistN(&ob->fmaps); - if (ob->pose) { - BKE_pose_free_ex(ob->pose, false); - ob->pose = NULL; - } - if (ob->mpath) { - animviz_free_motionpath(ob->mpath); - ob->mpath = NULL; - } - - BKE_constraints_free_ex(&ob->constraints, false); - - BKE_partdeflect_free(ob->pd); - BKE_rigidbody_free_object(ob, NULL); - BKE_rigidbody_free_constraint(ob); - - sbFree(ob); - - BKE_sculptsession_free(ob); - - BLI_freelistN(&ob->pc_ids); - - /* Free runtime curves data. */ - if (ob->runtime.curve_cache) { - BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); - if (ob->runtime.curve_cache->anim_path_accum_length) { - MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length); - } - MEM_freeN(ob->runtime.curve_cache); - ob->runtime.curve_cache = NULL; - } - - BKE_previewimg_free(&ob->preview); -} - -static void object_make_local(Main *bmain, ID *id, const int flags) -{ - if (!ID_IS_LINKED(id)) { - return; - } - - Object *ob = (Object *)id; - const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; - const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0; - bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; - bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; - BLI_assert(force_copy == false || force_copy != force_local); - - bool is_local = false, is_lib = false; - - /* - only lib users: do nothing (unless force_local is set) - * - only local users: set flag - * - mixed: make copy - * In case we make a whole lib's content local, - * we always want to localize, and we skip remapping (done later). - */ - - if (!force_local && !force_copy) { - BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); - if (lib_local || is_local) { - if (!is_lib) { - force_local = true; - } - else { - force_copy = true; - } - } - } - - if (force_local) { - BKE_lib_id_clear_library_data(bmain, &ob->id, flags); - BKE_lib_id_expand_local(bmain, &ob->id, flags); - if (clear_proxy) { - if (ob->proxy_from != NULL) { - ob->proxy_from->proxy = NULL; - ob->proxy_from->proxy_group = NULL; - } - ob->proxy = ob->proxy_from = ob->proxy_group = NULL; - } - } - else if (force_copy) { - Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id); - id_us_min(&ob_new->id); - - ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = NULL; - - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(ob, ob_new); - - if (!lib_local) { - BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE); - } - } -} - -static void library_foreach_modifiersForeachIDLink(void *user_data, - Object *UNUSED(object), - ID **id_pointer, - int cb_flag) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); -} - -static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, - Object *UNUSED(object), - ID **id_pointer, - int cb_flag) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); -} - -static void library_foreach_shaderfxForeachIDLink(void *user_data, - Object *UNUSED(object), - ID **id_pointer, - int cb_flag) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); -} - -static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), - ID **id_pointer, - bool is_reference, - void *user_data) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - const int cb_flag = is_reference ? IDWALK_CB_USER : IDWALK_CB_NOP; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); -} - -static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(psys), - ID **id_pointer, - void *user_data, - int cb_flag) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); -} - -static void object_foreach_id(ID *id, LibraryForeachIDData *data) -{ - Object *object = (Object *)id; - - /* Object is special, proxies make things hard... */ - const int proxy_cb_flag = ((BKE_lib_query_foreachid_process_flags_get(data) & - IDWALK_NO_INDIRECT_PROXY_DATA_USAGE) == 0 && - (object->proxy || object->proxy_group)) ? - IDWALK_CB_INDIRECT_USAGE : - 0; - - /* object data special case */ - if (object->type == OB_EMPTY) { - /* empty can have NULL or Image */ - BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, proxy_cb_flag | IDWALK_CB_USER); - } - else { - /* when set, this can't be NULL */ - if (object->data) { - BKE_LIB_FOREACHID_PROCESS_ID( - data, object->data, proxy_cb_flag | IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); - } - } - - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF); - /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP); - - /* Special case! - * Since this field is set/owned by 'user' of this ID (and not ID itself), - * it is only indirect usage if proxy object is linked... Twisted. */ - { - const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( - data, - (object->proxy_from != NULL && ID_IS_LINKED(object->proxy_from)) ? - IDWALK_CB_INDIRECT_USAGE : - 0, - true); - BKE_LIB_FOREACHID_PROCESS_IDSUPER( - data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); - BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); - } - - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER); - - for (int i = 0; i < object->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); - } - - /* Note that ob->gpd is deprecated, so no need to handle it here. */ - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->instance_collection, IDWALK_CB_USER); - - if (object->pd) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->f_source, IDWALK_CB_NOP); - } - /* Note that ob->effect is deprecated, so no need to handle it here. */ - - if (object->pose) { - const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( - data, proxy_cb_flag, false); - LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - IDP_foreach_property( - pchan->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pchan->custom, IDWALK_CB_USER); - BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, data); - } - BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); - } - - if (object->rigidbody_constraint) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER( - data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS_IDSUPER( - data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); - } - - BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data); - BKE_gpencil_modifiers_foreach_ID_link( - object, library_foreach_gpencil_modifiersForeachIDLink, data); - BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data); - BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data); - - LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { - BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data); - } - - if (object->soft) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->soft->collision_group, IDWALK_CB_NOP); - - if (object->soft->effector_weights) { - BKE_LIB_FOREACHID_PROCESS_IDSUPER( - data, object->soft->effector_weights->group, IDWALK_CB_NOP); - } - } -} - -static void write_fmaps(BlendWriter *writer, ListBase *fbase) -{ - LISTBASE_FOREACH (bFaceMap *, fmap, fbase) { - BLO_write_struct(writer, bFaceMap, fmap); - } -} - -static void object_blend_write(BlendWriter *writer, ID *id, const void *id_address) -{ - Object *ob = (Object *)id; - - const bool is_undo = BLO_write_is_undo(writer); - - /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ - BKE_object_runtime_reset(ob); - - if (is_undo) { - /* For undo we stay in object mode during undo presses, so keep edit-mode disabled on save as - * well, can help reducing false detection of changed data-blocks. */ - ob->mode &= ~OB_MODE_EDIT; - } - - /* write LibData */ - BLO_write_id_struct(writer, Object, id_address, &ob->id); - BKE_id_blend_write(writer, &ob->id); - - if (ob->adt) { - BKE_animdata_blend_write(writer, ob->adt); - } - - /* direct data */ - BLO_write_pointer_array(writer, ob->totcol, ob->mat); - BLO_write_raw(writer, sizeof(char) * ob->totcol, ob->matbits); - - bArmature *arm = NULL; - if (ob->type == OB_ARMATURE) { - arm = ob->data; - if (arm && ob->pose && arm->act_bone) { - BLI_strncpy(ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone)); - } - } - - BKE_pose_blend_write(writer, ob->pose, arm); - write_fmaps(writer, &ob->fmaps); - BKE_constraint_blend_write(writer, &ob->constraints); - animviz_motionpath_blend_write(writer, ob->mpath); - - BLO_write_struct(writer, PartDeflect, ob->pd); - if (ob->soft) { - /* Set deprecated pointers to prevent crashes of older Blenders */ - ob->soft->pointcache = ob->soft->shared->pointcache; - ob->soft->ptcaches = ob->soft->shared->ptcaches; - BLO_write_struct(writer, SoftBody, ob->soft); - BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared); - BKE_ptcache_blend_write(writer, &(ob->soft->shared->ptcaches)); - BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); - } - - if (ob->rigidbody_object) { - /* TODO: if any extra data is added to handle duplis, will need separate function then */ - BLO_write_struct(writer, RigidBodyOb, ob->rigidbody_object); - } - if (ob->rigidbody_constraint) { - BLO_write_struct(writer, RigidBodyCon, ob->rigidbody_constraint); - } - - if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { - BLO_write_struct(writer, ImageUser, ob->iuser); - } - - BKE_particle_system_blend_write(writer, &ob->particlesystem); - BKE_modifier_blend_write(writer, &ob->modifiers); - BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers); - BKE_shaderfx_blend_write(writer, &ob->shader_fx); - - BLO_write_struct_list(writer, LinkData, &ob->pc_ids); - - BKE_previewimg_blend_write(writer, ob->preview); -} - -/* XXX deprecated - old animation system */ -static void direct_link_nlastrips(BlendDataReader *reader, ListBase *strips) -{ - BLO_read_list(reader, strips); - - LISTBASE_FOREACH (bActionStrip *, strip, strips) { - BLO_read_list(reader, &strip->modifiers); - } -} - -static void object_blend_read_data(BlendDataReader *reader, ID *id) -{ - Object *ob = (Object *)id; - - PartEff *paf; - - /* XXX This should not be needed - but seems like it can happen in some cases, - * so for now play safe. */ - ob->proxy_from = NULL; - - const bool is_undo = BLO_read_data_is_undo(reader); - if (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT)) { - /* Do not allow any non-object mode for linked data. - * See T34776, T42780, T81027 for more information. */ - ob->mode &= ~OB_MODE_ALL_MODE_DATA; - } - else if (is_undo) { - /* For undo we want to stay in object mode during undo presses, so keep some edit modes - * disabled. - * TODO: Check if we should not disable more edit modes here? */ - ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT); - } - - BLO_read_data_address(reader, &ob->adt); - BKE_animdata_blend_read_data(reader, ob->adt); - - BLO_read_data_address(reader, &ob->pose); - BKE_pose_blend_read_data(reader, ob->pose); - - BLO_read_data_address(reader, &ob->mpath); - if (ob->mpath) { - animviz_motionpath_blend_read_data(reader, ob->mpath); - } - - /* Only for versioning, vertex group names are now stored on object data. */ - BLO_read_list(reader, &ob->defbase); - - BLO_read_list(reader, &ob->fmaps); - /* XXX deprecated - old animation system <<< */ - direct_link_nlastrips(reader, &ob->nlastrips); - BLO_read_list(reader, &ob->constraintChannels); - /* >>> XXX deprecated - old animation system */ - - BLO_read_pointer_array(reader, (void **)&ob->mat); - BLO_read_data_address(reader, &ob->matbits); - - /* do it here, below old data gets converted */ - BKE_modifier_blend_read_data(reader, &ob->modifiers, ob); - BKE_gpencil_modifier_blend_read_data(reader, &ob->greasepencil_modifiers); - BKE_shaderfx_blend_read_data(reader, &ob->shader_fx); - - BLO_read_list(reader, &ob->effect); - paf = ob->effect.first; - while (paf) { - if (paf->type == EFF_PARTICLE) { - paf->keys = NULL; - } - if (paf->type == EFF_WAVE) { - WaveEff *wav = (WaveEff *)paf; - PartEff *next = paf->next; - WaveModifierData *wmd = (WaveModifierData *)BKE_modifier_new(eModifierType_Wave); - - wmd->damp = wav->damp; - wmd->flag = wav->flag; - wmd->height = wav->height; - wmd->lifetime = wav->lifetime; - wmd->narrow = wav->narrow; - wmd->speed = wav->speed; - wmd->startx = wav->startx; - wmd->starty = wav->startx; - wmd->timeoffs = wav->timeoffs; - wmd->width = wav->width; - - BLI_addtail(&ob->modifiers, wmd); - - BLI_remlink(&ob->effect, paf); - MEM_freeN(paf); - - paf = next; - continue; - } - if (paf->type == EFF_BUILD) { - BuildEff *baf = (BuildEff *)paf; - PartEff *next = paf->next; - BuildModifierData *bmd = (BuildModifierData *)BKE_modifier_new(eModifierType_Build); - - bmd->start = baf->sfra; - bmd->length = baf->len; - bmd->randomize = 0; - bmd->seed = 1; - - BLI_addtail(&ob->modifiers, bmd); - - BLI_remlink(&ob->effect, paf); - MEM_freeN(paf); - - paf = next; - continue; - } - paf = paf->next; - } - - BLO_read_data_address(reader, &ob->pd); - BKE_particle_partdeflect_blend_read_data(reader, ob->pd); - BLO_read_data_address(reader, &ob->soft); - if (ob->soft) { - SoftBody *sb = ob->soft; - - sb->bpoint = NULL; /* init pointers so it gets rebuilt nicely */ - sb->bspring = NULL; - sb->scratch = NULL; - /* although not used anymore */ - /* still have to be loaded to be compatible with old files */ - BLO_read_pointer_array(reader, (void **)&sb->keys); - if (sb->keys) { - for (int a = 0; a < sb->totkey; a++) { - BLO_read_data_address(reader, &sb->keys[a]); - } - } - - BLO_read_data_address(reader, &sb->effector_weights); - if (!sb->effector_weights) { - sb->effector_weights = BKE_effector_add_weights(NULL); - } - - BLO_read_data_address(reader, &sb->shared); - if (sb->shared == NULL) { - /* Link deprecated caches if they exist, so we can use them for versioning. - * We should only do this when sb->shared == NULL, because those pointers - * are always set (for compatibility with older Blenders). We mustn't link - * the same pointcache twice. */ - BKE_ptcache_blend_read_data(reader, &sb->ptcaches, &sb->pointcache, false); - } - else { - /* link caches */ - BKE_ptcache_blend_read_data(reader, &sb->shared->ptcaches, &sb->shared->pointcache, false); - } - } - BLO_read_data_address(reader, &ob->fluidsimSettings); /* NT */ - - BLO_read_data_address(reader, &ob->rigidbody_object); - if (ob->rigidbody_object) { - RigidBodyOb *rbo = ob->rigidbody_object; - /* Allocate runtime-only struct */ - rbo->shared = MEM_callocN(sizeof(*rbo->shared), "RigidBodyObShared"); - } - BLO_read_data_address(reader, &ob->rigidbody_constraint); - if (ob->rigidbody_constraint) { - ob->rigidbody_constraint->physics_constraint = NULL; - } - - BLO_read_list(reader, &ob->particlesystem); - BKE_particle_system_blend_read_data(reader, &ob->particlesystem); - - BKE_constraint_blend_read_data(reader, &ob->constraints); - - BLO_read_list(reader, &ob->hooks); - while (ob->hooks.first) { - ObHook *hook = ob->hooks.first; - HookModifierData *hmd = (HookModifierData *)BKE_modifier_new(eModifierType_Hook); - - BLO_read_int32_array(reader, hook->totindex, &hook->indexar); - - /* Do conversion here because if we have loaded - * a hook we need to make sure it gets converted - * and freed, regardless of version. - */ - copy_v3_v3(hmd->cent, hook->cent); - hmd->falloff = hook->falloff; - hmd->force = hook->force; - hmd->indexar = hook->indexar; - hmd->object = hook->parent; - memcpy(hmd->parentinv, hook->parentinv, sizeof(hmd->parentinv)); - hmd->totindex = hook->totindex; - - BLI_addhead(&ob->modifiers, hmd); - BLI_remlink(&ob->hooks, hook); - - BKE_modifier_unique_name(&ob->modifiers, (ModifierData *)hmd); - - MEM_freeN(hook); - } - - BLO_read_data_address(reader, &ob->iuser); - if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE && !ob->iuser) { - BKE_object_empty_draw_type_set(ob, ob->empty_drawtype); - } - - BKE_object_runtime_reset(ob); - BLO_read_list(reader, &ob->pc_ids); - - /* in case this value changes in future, clamp else we get undefined behavior */ - CLAMP(ob->rotmode, ROT_MODE_MIN, ROT_MODE_MAX); - - if (ob->sculpt) { - ob->sculpt = NULL; - /* Only create data on undo, otherwise rely on editor mode switching. */ - if (BLO_read_data_is_undo(reader) && (ob->mode & OB_MODE_ALL_SCULPT)) { - BKE_object_sculpt_data_create(ob); - } - } - - BLO_read_data_address(reader, &ob->preview); - BKE_previewimg_blend_read(reader, ob->preview); -} - -/* XXX deprecated - old animation system */ -static void lib_link_nlastrips(BlendLibReader *reader, ID *id, ListBase *striplist) -{ - LISTBASE_FOREACH (bActionStrip *, strip, striplist) { - BLO_read_id_address(reader, id->lib, &strip->object); - BLO_read_id_address(reader, id->lib, &strip->act); - BLO_read_id_address(reader, id->lib, &strip->ipo); - LISTBASE_FOREACH (bActionModifier *, amod, &strip->modifiers) { - BLO_read_id_address(reader, id->lib, &amod->ob); - } - } -} - -/* XXX deprecated - old animation system */ -static void lib_link_constraint_channels(BlendLibReader *reader, ID *id, ListBase *chanbase) -{ - LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) { - BLO_read_id_address(reader, id->lib, &chan->ipo); - } -} - -static void object_blend_read_lib(BlendLibReader *reader, ID *id) -{ - Object *ob = (Object *)id; - - Main *bmain = BLO_read_lib_get_main(reader); - BlendFileReadReport *reports = BLO_read_lib_reports(reader); - - /* XXX deprecated - old animation system <<< */ - BLO_read_id_address(reader, ob->id.lib, &ob->ipo); - BLO_read_id_address(reader, ob->id.lib, &ob->action); - /* >>> XXX deprecated - old animation system */ - - BLO_read_id_address(reader, ob->id.lib, &ob->parent); - BLO_read_id_address(reader, ob->id.lib, &ob->track); - BLO_read_id_address(reader, ob->id.lib, &ob->poselib); - - /* 2.8x drops support for non-empty dupli instances. */ - if (ob->type == OB_EMPTY) { - BLO_read_id_address(reader, ob->id.lib, &ob->instance_collection); - } - else { - if (ob->instance_collection != NULL) { - ID *new_id = BLO_read_get_new_id_address(reader, ob->id.lib, &ob->instance_collection->id); - BLO_reportf_wrap(reports, - RPT_INFO, - TIP_("Non-Empty object '%s' cannot duplicate collection '%s' " - "anymore in Blender 2.80, removed instancing"), - ob->id.name + 2, - new_id->name + 2); - } - ob->instance_collection = NULL; - ob->transflag &= ~OB_DUPLICOLLECTION; - } - - BLO_read_id_address(reader, ob->id.lib, &ob->proxy); - if (ob->proxy) { - /* paranoia check, actually a proxy_from pointer should never be written... */ - if (!ID_IS_LINKED(ob->proxy)) { - ob->proxy->proxy_from = NULL; - ob->proxy = NULL; - - if (ob->id.lib) { - BLO_reportf_wrap(reports, - RPT_INFO, - TIP_("Proxy lost from object %s lib %s\n"), - ob->id.name + 2, - ob->id.lib->filepath); - } - else { - BLO_reportf_wrap( - reports, RPT_INFO, TIP_("Proxy lost from object %s lib \n"), ob->id.name + 2); - } - reports->count.missing_obproxies++; - } - else { - /* this triggers object_update to always use a copy */ - ob->proxy->proxy_from = ob; - } - } - BLO_read_id_address(reader, ob->id.lib, &ob->proxy_group); - - void *poin = ob->data; - BLO_read_id_address(reader, ob->id.lib, &ob->data); - - if (ob->data == NULL && poin != NULL) { - ob->type = OB_EMPTY; - - if (ob->pose) { - /* we can't call #BKE_pose_free() here because of library linking - * freeing will recurse down into every pose constraints ID pointers - * which are not always valid, so for now free directly and suffer - * some leaked memory rather than crashing immediately - * while bad this _is_ an exceptional case - campbell */ -#if 0 - BKE_pose_free(ob->pose); -#else - MEM_freeN(ob->pose); -#endif - ob->pose = NULL; - ob->mode &= ~OB_MODE_POSE; - } - - if (ob->id.lib) { - BLO_reportf_wrap(reports, - RPT_INFO, - TIP_("Can't find object data of %s lib %s\n"), - ob->id.name + 2, - ob->id.lib->filepath); - } - else { - BLO_reportf_wrap(reports, RPT_INFO, TIP_("Object %s lost data\n"), ob->id.name + 2); - } - reports->count.missing_obdata++; - } - for (int a = 0; a < ob->totcol; a++) { - BLO_read_id_address(reader, ob->id.lib, &ob->mat[a]); - } - - /* When the object is local and the data is library its possible - * the material list size gets out of sync. T22663. */ - if (ob->data && ob->id.lib != ((ID *)ob->data)->lib) { - BKE_object_materials_test(bmain, ob, ob->data); - } - - BLO_read_id_address(reader, ob->id.lib, &ob->gpd); - - /* if id.us==0 a new base will be created later on */ - - /* WARNING! Also check expand_object(), should reflect the stuff below. */ - BKE_pose_blend_read_lib(reader, ob, ob->pose); - BKE_constraint_blend_read_lib(reader, &ob->id, &ob->constraints); - - /* XXX deprecated - old animation system <<< */ - lib_link_constraint_channels(reader, &ob->id, &ob->constraintChannels); - lib_link_nlastrips(reader, &ob->id, &ob->nlastrips); - /* >>> XXX deprecated - old animation system */ - - LISTBASE_FOREACH (PartEff *, paf, &ob->effect) { - if (paf->type == EFF_PARTICLE) { - BLO_read_id_address(reader, ob->id.lib, &paf->group); - } - } - - { - FluidsimModifierData *fluidmd = (FluidsimModifierData *)BKE_modifiers_findby_type( - ob, eModifierType_Fluidsim); - - if (fluidmd && fluidmd->fss) { - /* XXX: deprecated - old animation system. */ - BLO_read_id_address(reader, ob->id.lib, &fluidmd->fss->ipo); - } - } - - { - FluidModifierData *fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, - eModifierType_Fluid); - - if (fmd && (fmd->type == MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { - /* Flag for refreshing the simulation after loading */ - fmd->domain->flags |= FLUID_DOMAIN_FILE_LOAD; - } - else if (fmd && (fmd->type == MOD_FLUID_TYPE_FLOW) && fmd->flow) { - fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE; - } - else if (fmd && (fmd->type == MOD_FLUID_TYPE_EFFEC) && fmd->effector) { - fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE; - } - } - - /* texture field */ - if (ob->pd) { - BKE_particle_partdeflect_blend_read_lib(reader, &ob->id, ob->pd); - } - - if (ob->soft) { - BLO_read_id_address(reader, ob->id.lib, &ob->soft->collision_group); - - BLO_read_id_address(reader, ob->id.lib, &ob->soft->effector_weights->group); - } - - BKE_particle_system_blend_read_lib(reader, ob, &ob->id, &ob->particlesystem); - BKE_modifier_blend_read_lib(reader, ob); - BKE_gpencil_modifier_blend_read_lib(reader, ob); - BKE_shaderfx_blend_read_lib(reader, ob); - - if (ob->rigidbody_constraint) { - BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob1); - BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob2); - } -} - -/* XXX deprecated - old animation system */ -static void expand_constraint_channels(BlendExpander *expander, ListBase *chanbase) -{ - LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) { - BLO_expand(expander, chan->ipo); - } -} - -static void expand_object_expandModifiers(void *userData, - Object *UNUSED(ob), - ID **idpoin, - int UNUSED(cb_flag)) -{ - BlendExpander *expander = userData; - BLO_expand(expander, *idpoin); -} - -PartEff *BKE_object_do_version_give_parteff_245(Object *ob) -{ - PartEff *paf; - - paf = ob->effect.first; - while (paf) { - if (paf->type == EFF_PARTICLE) { - return paf; - } - paf = paf->next; - } - return NULL; -} - -static void object_blend_read_expand(BlendExpander *expander, ID *id) -{ - Object *ob = (Object *)id; - - BLO_expand(expander, ob->data); - - BLO_expand(expander, ob->parent); - - /* expand_object_expandModifier() */ - if (ob->modifiers.first) { - BKE_modifiers_foreach_ID_link(ob, expand_object_expandModifiers, expander); - } - - /* expand_object_expandModifier() */ - if (ob->greasepencil_modifiers.first) { - BKE_gpencil_modifiers_foreach_ID_link(ob, expand_object_expandModifiers, expander); - } - - /* expand_object_expandShaderFx() */ - if (ob->shader_fx.first) { - BKE_shaderfx_foreach_ID_link(ob, expand_object_expandModifiers, expander); - } - - BKE_pose_blend_read_expand(expander, ob->pose); - BLO_expand(expander, ob->poselib); - BKE_constraint_blend_read_expand(expander, &ob->constraints); - - BLO_expand(expander, ob->gpd); - - /* XXX deprecated - old animation system (for version patching only) */ - BLO_expand(expander, ob->ipo); - BLO_expand(expander, ob->action); - - expand_constraint_channels(expander, &ob->constraintChannels); - - LISTBASE_FOREACH (bActionStrip *, strip, &ob->nlastrips) { - BLO_expand(expander, strip->object); - BLO_expand(expander, strip->act); - BLO_expand(expander, strip->ipo); - } - /* XXX deprecated - old animation system (for version patching only) */ - - for (int a = 0; a < ob->totcol; a++) { - BLO_expand(expander, ob->mat[a]); - } - - PartEff *paf = BKE_object_do_version_give_parteff_245(ob); - if (paf && paf->group) { - BLO_expand(expander, paf->group); - } - - if (ob->instance_collection) { - BLO_expand(expander, ob->instance_collection); - } - - if (ob->proxy) { - BLO_expand(expander, ob->proxy); - } - if (ob->proxy_group) { - BLO_expand(expander, ob->proxy_group); - } - - LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { - BLO_expand(expander, psys->part); - } - - if (ob->pd) { - BLO_expand(expander, ob->pd->tex); - BLO_expand(expander, ob->pd->f_source); - } - - if (ob->soft) { - BLO_expand(expander, ob->soft->collision_group); - - if (ob->soft->effector_weights) { - BLO_expand(expander, ob->soft->effector_weights->group); - } - } - - if (ob->rigidbody_constraint) { - BLO_expand(expander, ob->rigidbody_constraint->ob1); - BLO_expand(expander, ob->rigidbody_constraint->ob2); - } -} - -static void object_lib_override_apply_post(ID *id_dst, ID *id_src) -{ - /* id_dst is the new local override copy of the linked reference data. id_src is the old override - * data stored on disk, used as source data for override operations. */ - Object *object_dst = (Object *)id_dst; - Object *object_src = (Object *)id_src; - - ListBase pidlist_dst, pidlist_src; - BKE_ptcache_ids_from_object(&pidlist_dst, object_dst, NULL, 0); - BKE_ptcache_ids_from_object(&pidlist_src, object_src, NULL, 0); - - /* Problem with point caches is that several status flags (like OUTDATED or BAKED) are read-only - * at RNA level, and therefore not overridable per-se. - * - * This code is a workaround this to check all point-caches from both source and destination - * objects in parallel, and transfer those flags when it makes sense. - * - * This allows to keep baked caches across liboverrides applies. - * - * NOTE: This is fairly hackish and weak, but so is the point-cache system as its whole. A more - * robust solution would be e.g. to have a specific RNA entry point to deal with such cases - * (maybe a new flag to allow override code to set values of some read-only properties?). - */ - PTCacheID *pid_src, *pid_dst; - for (pid_dst = pidlist_dst.first, pid_src = pidlist_src.first; pid_dst != NULL; - pid_dst = pid_dst->next, pid_src = (pid_src != NULL) ? pid_src->next : NULL) { - /* If pid's do not match, just tag info of caches in dst as dirty and continue. */ - if (pid_src == NULL) { - continue; - } - if (pid_dst->type != pid_src->type || pid_dst->file_type != pid_src->file_type || - pid_dst->default_step != pid_src->default_step || pid_dst->max_step != pid_src->max_step || - pid_dst->data_types != pid_src->data_types || pid_dst->info_types != pid_src->info_types) { - LISTBASE_FOREACH (PointCache *, point_cache_src, pid_src->ptcaches) { - point_cache_src->flag |= PTCACHE_FLAG_INFO_DIRTY; - } - continue; - } - - PointCache *point_cache_dst, *point_cache_src; - for (point_cache_dst = pid_dst->ptcaches->first, point_cache_src = pid_src->ptcaches->first; - point_cache_dst != NULL; - point_cache_dst = point_cache_dst->next, - point_cache_src = (point_cache_src != NULL) ? point_cache_src->next : NULL) { - /* Always force updating info about caches of applied liboverrides. */ - point_cache_dst->flag |= PTCACHE_FLAG_INFO_DIRTY; - if (point_cache_src == NULL || !STREQ(point_cache_dst->name, point_cache_src->name)) { - continue; - } - if ((point_cache_src->flag & PTCACHE_BAKED) != 0) { - point_cache_dst->flag |= PTCACHE_BAKED; - } - if ((point_cache_src->flag & PTCACHE_OUTDATED) == 0) { - point_cache_dst->flag &= ~PTCACHE_OUTDATED; - } - } - } - BLI_freelistN(&pidlist_dst); - BLI_freelistN(&pidlist_src); -} - -static IDProperty *object_asset_dimensions_property(Object *ob) -{ - float dimensions[3]; - BKE_object_dimensions_get(ob, dimensions); - if (is_zero_v3(dimensions)) { - return NULL; - } - - IDPropertyTemplate idprop = {0}; - idprop.array.len = ARRAY_SIZE(dimensions); - idprop.array.type = IDP_FLOAT; - - IDProperty *property = IDP_New(IDP_ARRAY, &idprop, "dimensions"); - memcpy(IDP_Array(property), dimensions, sizeof(dimensions)); - - return property; -} - -static void object_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data) -{ - Object *ob = asset_ptr; - BLI_assert(GS(ob->id.name) == ID_OB); - - /* Update dimensions hint for the asset. */ - IDProperty *dimensions_prop = object_asset_dimensions_property(ob); - if (dimensions_prop) { - BKE_asset_metadata_idprop_ensure(asset_data, dimensions_prop); - } -} - -AssetTypeInfo AssetType_OB = { - .pre_save_fn = object_asset_pre_save, -}; - -IDTypeInfo IDType_ID_OB = { - .id_code = ID_OB, - .id_filter = FILTER_ID_OB, - .main_listbase_index = INDEX_ID_OB, - .struct_size = sizeof(Object), - .name = "Object", - .name_plural = "objects", - .translation_context = BLT_I18NCONTEXT_ID_OBJECT, - .flags = 0, - - .init_data = object_init_data, - .copy_data = object_copy_data, - .free_data = object_free_data, - .make_local = object_make_local, - .foreach_id = object_foreach_id, - .foreach_cache = NULL, - .owner_get = NULL, - - .blend_write = object_blend_write, - .blend_read_data = object_blend_read_data, - .blend_read_lib = object_blend_read_lib, - .blend_read_expand = object_blend_read_expand, - - .blend_read_undo_preserve = NULL, - - .lib_override_apply_post = object_lib_override_apply_post, - - .asset_type_info = &AssetType_OB, -}; - -void BKE_object_workob_clear(Object *workob) -{ - memset(workob, 0, sizeof(Object)); - - workob->scale[0] = workob->scale[1] = workob->scale[2] = 1.0f; - workob->dscale[0] = workob->dscale[1] = workob->dscale[2] = 1.0f; - workob->rotmode = ROT_MODE_EUL; -} - -void BKE_object_free_particlesystems(Object *ob) -{ - ParticleSystem *psys; - - while ((psys = BLI_pophead(&ob->particlesystem))) { - psys_free(ob, psys); - } -} - -void BKE_object_free_softbody(Object *ob) -{ - sbFree(ob); -} - -void BKE_object_free_curve_cache(Object *ob) -{ - if (ob->runtime.curve_cache) { - BKE_displist_free(&ob->runtime.curve_cache->disp); - BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); - if (ob->runtime.curve_cache->anim_path_accum_length) { - MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length); - } - BKE_nurbList_free(&ob->runtime.curve_cache->deformed_nurbs); - MEM_freeN(ob->runtime.curve_cache); - ob->runtime.curve_cache = NULL; - } -} - -void BKE_object_free_modifiers(Object *ob, const int flag) -{ - ModifierData *md; - GpencilModifierData *gp_md; - - while ((md = BLI_pophead(&ob->modifiers))) { - BKE_modifier_free_ex(md, flag); - } - - while ((gp_md = BLI_pophead(&ob->greasepencil_modifiers))) { - BKE_gpencil_modifier_free_ex(gp_md, flag); - } - /* particle modifiers were freed, so free the particlesystems as well */ - BKE_object_free_particlesystems(ob); - - /* same for softbody */ - BKE_object_free_softbody(ob); - - /* modifiers may have stored data in the DM cache */ - BKE_object_free_derived_caches(ob); -} - -void BKE_object_free_shaderfx(Object *ob, const int flag) -{ - ShaderFxData *fx; - - while ((fx = BLI_pophead(&ob->shader_fx))) { - BKE_shaderfx_free_ex(fx, flag); - } -} - -void BKE_object_modifier_hook_reset(Object *ob, HookModifierData *hmd) -{ - /* reset functionality */ - if (hmd->object) { - bPoseChannel *pchan = BKE_pose_channel_find_name(hmd->object->pose, hmd->subtarget); - - if (hmd->subtarget[0] && pchan) { - float imat[4][4], mat[4][4]; - - /* Calculate the world-space matrix for the pose-channel target first, - * then carry on as usual. */ - mul_m4_m4m4(mat, hmd->object->obmat, pchan->pose_mat); - - invert_m4_m4(imat, mat); - mul_m4_m4m4(hmd->parentinv, imat, ob->obmat); - } - else { - invert_m4_m4(hmd->object->imat, hmd->object->obmat); - mul_m4_m4m4(hmd->parentinv, hmd->object->imat, ob->obmat); - } - } -} - -void BKE_object_modifier_gpencil_hook_reset(Object *ob, HookGpencilModifierData *hmd) -{ - if (hmd->object == NULL) { - return; - } - /* reset functionality */ - bPoseChannel *pchan = BKE_pose_channel_find_name(hmd->object->pose, hmd->subtarget); - - if (hmd->subtarget[0] && pchan) { - float imat[4][4], mat[4][4]; - - /* Calculate the world-space matrix for the pose-channel target first, - * then carry on as usual. */ - mul_m4_m4m4(mat, hmd->object->obmat, pchan->pose_mat); - - invert_m4_m4(imat, mat); - mul_m4_m4m4(hmd->parentinv, imat, ob->obmat); - } - else { - invert_m4_m4(hmd->object->imat, hmd->object->obmat); - mul_m4_m4m4(hmd->parentinv, hmd->object->imat, ob->obmat); - } -} - -/** - * Set the object's active modifier. - * - * \param md: If NULL, only clear the active modifier, otherwise - * it must be in the #Object.modifiers list. - */ -void BKE_object_modifier_set_active(Object *ob, ModifierData *md) -{ - LISTBASE_FOREACH (ModifierData *, md_iter, &ob->modifiers) { - md_iter->flag &= ~eModifierFlag_Active; - } - - if (md != NULL) { - BLI_assert(BLI_findindex(&ob->modifiers, md) != -1); - md->flag |= eModifierFlag_Active; - } -} - -ModifierData *BKE_object_active_modifier(const Object *ob) -{ - /* In debug mode, check for only one active modifier. */ -#ifndef NDEBUG - int active_count = 0; - LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { - if (md->flag & eModifierFlag_Active) { - active_count++; - } - } - BLI_assert(ELEM(active_count, 0, 1)); -#endif - - LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { - if (md->flag & eModifierFlag_Active) { - return md; - } - } - - return NULL; -} - -/** - * \return True if the object's type supports regular modifiers (not grease pencil modifiers). - */ -bool BKE_object_supports_modifiers(const Object *ob) -{ - return ( - ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE, OB_POINTCLOUD, OB_VOLUME)); -} - -bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) -{ - const ModifierTypeInfo *mti = BKE_modifier_get_info(modifier_type); - - /* Surface and lattice objects don't output geometry sets. */ - if (mti->modifyGeometrySet != NULL && ELEM(ob->type, OB_SURF, OB_LATTICE)) { - return false; - } - - /* Only geometry objects should be able to get modifiers T25291. */ - if (ob->type == OB_HAIR) { - return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); - } - if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) { - return (mti->modifyGeometrySet != NULL); - } - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { - if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) { - return false; - } - - if (!((mti->flags & eModifierTypeFlag_AcceptsCVs) || - (ob->type == OB_MESH && (mti->flags & eModifierTypeFlag_AcceptsMesh)))) { - return false; - } - - return true; - } - - return false; -} - -static bool object_modifier_type_copy_check(ModifierType md_type) -{ - return !ELEM(md_type, eModifierType_Hook, eModifierType_Collision); -} - -/** - * Find a `psys` matching given `psys_src` in `ob_dst` (i.e. sharing the same ParticleSettings ID), - * or add one, and return valid `psys` from `ob_dst`. - * - * \note Order handling is fairly weak here. This code assumes that it is called **before** the - * modifier using the psys is actually copied, and that this copied modifier will be added at the - * end of the stack. That way we can be sure that the particle modifier will be before the one - * using its particle system in the stack. - */ -static ParticleSystem *object_copy_modifier_particle_system_ensure(Main *bmain, - Scene *scene, - Object *ob_dst, - ParticleSystem *psys_src) -{ - ParticleSystem *psys_dst = NULL; - - /* Check if a particle system with the same particle settings - * already exists on the destination object. */ - LISTBASE_FOREACH (ParticleSystem *, psys, &ob_dst->particlesystem) { - if (psys->part == psys_src->part) { - psys_dst = psys; - break; - } - } - - /* If it does not exist, copy the particle system to the destination object. */ - if (psys_dst == NULL) { - ModifierData *md = object_copy_particle_system(bmain, scene, ob_dst, psys_src); - psys_dst = ((ParticleSystemModifierData *)md)->psys; - } - - return psys_dst; -} - -/** - * Copy a single modifier. - * - * \note **Do not** use this function to copy a whole modifier stack (see note below too). Use - * `BKE_object_modifier_stack_copy` instead. - * - * \note Complex modifiers relaying on other data (like e.g. dynamic paint or fluid using particle - * systems) are not always 100% 'correctly' copied here, since we have to use heuristics to decide - * which particle system to use or add in `ob_dst`, and it's placement in the stack, etc. If used - * more than once, this function should preferably be called in stack order. - */ -bool BKE_object_copy_modifier( - Main *bmain, Scene *scene, Object *ob_dst, const Object *ob_src, ModifierData *md_src) -{ - BLI_assert(ob_dst->type != OB_GPENCIL); - - const ModifierTypeInfo *mti = BKE_modifier_get_info(md_src->type); - if (!object_modifier_type_copy_check(md_src->type)) { - /* We never allow copying those modifiers here. */ - return false; - } - if (!BKE_object_support_modifier_type_check(ob_dst, md_src->type)) { - return false; - } - if (mti->flags & eModifierTypeFlag_Single) { - if (BKE_modifiers_findby_type(ob_dst, md_src->type) != NULL) { - return false; - } - } - - ParticleSystem *psys_src = NULL; - ParticleSystem *psys_dst = NULL; - - switch (md_src->type) { - case eModifierType_Softbody: - BKE_object_copy_softbody(ob_dst, ob_src, 0); - break; - case eModifierType_Skin: - /* ensure skin-node customdata exists */ - BKE_mesh_ensure_skin_customdata(ob_dst->data); - break; - case eModifierType_Fluid: { - FluidModifierData *fmd = (FluidModifierData *)md_src; - if (fmd->type == MOD_FLUID_TYPE_FLOW) { - if (fmd->flow != NULL && fmd->flow->psys != NULL) { - psys_src = fmd->flow->psys; - psys_dst = object_copy_modifier_particle_system_ensure(bmain, scene, ob_dst, psys_src); - } - } - break; - } - case eModifierType_DynamicPaint: { - DynamicPaintModifierData *dpmd = (DynamicPaintModifierData *)md_src; - if (dpmd->brush != NULL && dpmd->brush->psys != NULL) { - psys_src = dpmd->brush->psys; - psys_dst = object_copy_modifier_particle_system_ensure(bmain, scene, ob_dst, psys_src); - } - break; - } - default: - break; - } - - ModifierData *md_dst; - if (md_src->type == eModifierType_ParticleSystem) { - md_dst = object_copy_particle_system( - bmain, scene, ob_dst, ((ParticleSystemModifierData *)md_src)->psys); - } - else { - md_dst = BKE_modifier_new(md_src->type); - - BLI_strncpy(md_dst->name, md_src->name, sizeof(md_dst->name)); - - if (md_src->type == eModifierType_Multires) { - /* Has to be done after mod creation, but *before* we actually copy its settings! */ - multiresModifier_sync_levels_ex( - ob_dst, (MultiresModifierData *)md_src, (MultiresModifierData *)md_dst); - } - - BKE_modifier_copydata(md_src, md_dst); - - switch (md_dst->type) { - case eModifierType_Fluid: - if (psys_dst != NULL) { - FluidModifierData *fmd_dst = (FluidModifierData *)md_dst; - BLI_assert(fmd_dst->type == MOD_FLUID_TYPE_FLOW && fmd_dst->flow != NULL && - fmd_dst->flow->psys != NULL); - fmd_dst->flow->psys = psys_dst; - } - break; - case eModifierType_DynamicPaint: - if (psys_dst != NULL) { - DynamicPaintModifierData *dpmd_dst = (DynamicPaintModifierData *)md_dst; - BLI_assert(dpmd_dst->brush != NULL && dpmd_dst->brush->psys != NULL); - dpmd_dst->brush->psys = psys_dst; - } - break; - default: - break; - } - - BLI_addtail(&ob_dst->modifiers, md_dst); - BKE_modifier_unique_name(&ob_dst->modifiers, md_dst); - } - - BKE_object_modifier_set_active(ob_dst, md_dst); - - return true; -} - -/** - * Copy a single GPencil modifier. - * - * \note **Do not** use this function to copy a whole modifier stack. Use - * `BKE_object_modifier_stack_copy` instead. - */ -bool BKE_object_copy_gpencil_modifier(struct Object *ob_dst, GpencilModifierData *gmd_src) -{ - BLI_assert(ob_dst->type == OB_GPENCIL); - - GpencilModifierData *gmd_dst = BKE_gpencil_modifier_new(gmd_src->type); - BLI_strncpy(gmd_dst->name, gmd_src->name, sizeof(gmd_dst->name)); - - const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(gmd_src->type); - mti->copyData(gmd_src, gmd_dst); - - BLI_addtail(&ob_dst->greasepencil_modifiers, gmd_dst); - BKE_gpencil_modifier_unique_name(&ob_dst->greasepencil_modifiers, gmd_dst); - - return true; -} - -/** - * Copy the whole stack of modifiers from one object into another. - * - * \warning **Does not** clear modifier stack and related data (particle systems, soft-body, - * etc.) in `ob_dst`, if needed calling code must do it. - * - * \param do_copy_all: If true, even modifiers that should not support copying (like Hook one) - * will be duplicated. - */ -bool BKE_object_modifier_stack_copy(Object *ob_dst, - const Object *ob_src, - const bool do_copy_all, - const int flag_subdata) -{ - if ((ob_dst->type == OB_GPENCIL) != (ob_src->type == OB_GPENCIL)) { - BLI_assert_msg(0, - "Trying to copy a modifier stack between a GPencil object and another type."); - return false; - } - - if (!BLI_listbase_is_empty(&ob_dst->modifiers) || - !BLI_listbase_is_empty(&ob_dst->greasepencil_modifiers)) { - BLI_assert( - !"Trying to copy a modifier stack into an object having a non-empty modifier stack."); - return false; - } - - LISTBASE_FOREACH (ModifierData *, md_src, &ob_src->modifiers) { - if (!do_copy_all && !object_modifier_type_copy_check(md_src->type)) { - continue; - } - if (!BKE_object_support_modifier_type_check(ob_dst, md_src->type)) { - continue; - } - - ModifierData *md_dst = BKE_modifier_new(md_src->type); - BLI_strncpy(md_dst->name, md_src->name, sizeof(md_dst->name)); - BKE_modifier_copydata_ex(md_src, md_dst, flag_subdata); - BLI_addtail(&ob_dst->modifiers, md_dst); - } - - LISTBASE_FOREACH (GpencilModifierData *, gmd_src, &ob_src->greasepencil_modifiers) { - GpencilModifierData *gmd_dst = BKE_gpencil_modifier_new(gmd_src->type); - BLI_strncpy(gmd_dst->name, gmd_src->name, sizeof(gmd_dst->name)); - BKE_gpencil_modifier_copydata_ex(gmd_src, gmd_dst, flag_subdata); - BLI_addtail(&ob_dst->greasepencil_modifiers, gmd_dst); - } - - /* This could be copied from anywhere, since no other modifier actually use this data. But for - * consistency do it together with particle systems. */ - BKE_object_copy_softbody(ob_dst, ob_src, flag_subdata); - - /* It is mandatory that this happens after copying modifiers, as it will update their `psys` - * pointers accordingly. */ - BKE_object_copy_particlesystems(ob_dst, ob_src, flag_subdata); - - return true; -} - -void BKE_object_link_modifiers(Object *ob_dst, const Object *ob_src) -{ - BKE_object_free_modifiers(ob_dst, 0); - - BKE_object_modifier_stack_copy(ob_dst, ob_src, false, 0); -} - -/** - * Copy CCG related data. Used to sync copy of mesh with reshaped original mesh. - */ -static void copy_ccg_data(Mesh *mesh_destination, Mesh *mesh_source, int layer_type) -{ - BLI_assert(mesh_destination->totloop == mesh_source->totloop); - CustomData *data_destination = &mesh_destination->ldata; - CustomData *data_source = &mesh_source->ldata; - const int num_elements = mesh_source->totloop; - if (!CustomData_has_layer(data_source, layer_type)) { - return; - } - const int layer_index = CustomData_get_layer_index(data_destination, layer_type); - CustomData_free_layer(data_destination, layer_type, num_elements, layer_index); - BLI_assert(!CustomData_has_layer(data_destination, layer_type)); - CustomData_add_layer(data_destination, layer_type, CD_CALLOC, NULL, num_elements); - BLI_assert(CustomData_has_layer(data_destination, layer_type)); - CustomData_copy_layer_type_data(data_source, data_destination, layer_type, 0, 0, num_elements); -} - -static void object_update_from_subsurf_ccg(Object *object) -{ - /* Currently CCG is only created for Mesh objects. */ - if (object->type != OB_MESH) { - return; - } - /* If object does not own evaluated mesh we can not access it since it might be freed already - * (happens on dependency graph free where order of CoW-ed IDs free is undefined). - * - * Good news is: such mesh does not have modifiers applied, so no need to worry about CCG. */ - if (!object->runtime.is_data_eval_owned) { - return; - } - /* Object was never evaluated, so can not have CCG subdivision surface. */ - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object); - if (mesh_eval == NULL) { - return; - } - SubdivCCG *subdiv_ccg = mesh_eval->runtime.subdiv_ccg; - if (subdiv_ccg == NULL) { - return; - } - /* Check whether there is anything to be reshaped. */ - if (!subdiv_ccg->dirty.coords && !subdiv_ccg->dirty.hidden) { - return; - } - const int tot_level = mesh_eval->runtime.subdiv_ccg_tot_level; - Object *object_orig = DEG_get_original_object(object); - Mesh *mesh_orig = (Mesh *)object_orig->data; - multiresModifier_reshapeFromCCG(tot_level, mesh_orig, subdiv_ccg); - /* NOTE: we need to reshape into an original mesh from main database, - * allowing: - * - * - Update copies of that mesh at any moment. - * - Save the file without doing extra reshape. - * - All the users of the mesh have updated displacement. - * - * However, the tricky part here is that we only know about sculpted - * state of a mesh on an object level, and object is being updated after - * mesh datablock is updated. This forces us to: - * - * - Update mesh datablock from object evaluation, which is technically - * forbidden, but there is no other place for this yet. - * - Reshape to the original mesh from main database, and then copy updated - * layer to copy of that mesh (since copy of the mesh has decoupled - * custom data layers). - * - * All this is defeating all the designs we need to follow to allow safe - * threaded evaluation, but this is as good as we can make it within the - * current sculpt/evaluated mesh design. This is also how we've survived - * with old DerivedMesh based solutions. So, while this is all wrong and - * needs reconsideration, doesn't seem to be a big stopper for real - * production artists. - */ - /* TODO(sergey): Solve this somehow, to be fully stable for threaded - * evaluation environment. - */ - /* NOTE: runtime.data_orig is what was before assigning mesh_eval, - * it is orig as in what was in object_eval->data before evaluating - * modifier stack. - * - * mesh_cow is a copy-on-written version od object_orig->data. - */ - Mesh *mesh_cow = (Mesh *)object->runtime.data_orig; - copy_ccg_data(mesh_cow, mesh_orig, CD_MDISPS); - copy_ccg_data(mesh_cow, mesh_orig, CD_GRID_PAINT_MASK); - /* Everything is now up-to-date. */ - subdiv_ccg->dirty.coords = false; - subdiv_ccg->dirty.hidden = false; -} - -/** - * Assign #Object.data after modifier stack evaluation. - */ -void BKE_object_eval_assign_data(Object *object_eval, ID *data_eval, bool is_owned) -{ - BLI_assert(object_eval->id.tag & LIB_TAG_COPIED_ON_WRITE); - BLI_assert(object_eval->runtime.data_eval == NULL); - BLI_assert(data_eval->tag & LIB_TAG_NO_MAIN); - - if (is_owned) { - /* Set flag for debugging. */ - data_eval->tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT; - } - - /* Assigned evaluated data. */ - object_eval->runtime.data_eval = data_eval; - object_eval->runtime.is_data_eval_owned = is_owned; - - /* Overwrite data of evaluated object, if the datablock types match. */ - ID *data = object_eval->data; - if (GS(data->name) == GS(data_eval->name)) { - /* NOTE: we are not supposed to invoke evaluation for original objects, - * but some areas are still being ported, so we play safe here. */ - if (object_eval->id.tag & LIB_TAG_COPIED_ON_WRITE) { - object_eval->data = data_eval; - } - } - - /* Is set separately currently. */ - object_eval->runtime.geometry_set_eval = NULL; -} - -/** - * Free data derived from mesh, called when mesh changes or is freed. - */ -void BKE_object_free_derived_caches(Object *ob) -{ - MEM_SAFE_FREE(ob->runtime.bb); - - object_update_from_subsurf_ccg(ob); - - if (ob->runtime.data_eval != NULL) { - if (ob->runtime.is_data_eval_owned) { - ID *data_eval = ob->runtime.data_eval; - if (GS(data_eval->name) == ID_ME) { - BKE_mesh_eval_delete((Mesh *)data_eval); - } - else { - BKE_libblock_free_datablock(data_eval, 0); - MEM_freeN(data_eval); - } - } - ob->runtime.data_eval = NULL; - } - if (ob->runtime.mesh_deform_eval != NULL) { - Mesh *mesh_deform_eval = ob->runtime.mesh_deform_eval; - BKE_mesh_eval_delete(mesh_deform_eval); - ob->runtime.mesh_deform_eval = NULL; - } - - /* Restore initial pointer for copy-on-write datablocks, object->data - * might be pointing to an evaluated datablock data was just freed above. */ - if (ob->runtime.data_orig != NULL) { - ob->data = ob->runtime.data_orig; - } - - BKE_object_to_mesh_clear(ob); - BKE_object_to_curve_clear(ob); - BKE_object_free_curve_cache(ob); - - /* Clear grease pencil data. */ - if (ob->runtime.gpd_eval != NULL) { - BKE_gpencil_eval_delete(ob->runtime.gpd_eval); - ob->runtime.gpd_eval = NULL; - } - - if (ob->runtime.geometry_set_eval != NULL) { - BKE_geometry_set_free(ob->runtime.geometry_set_eval); - ob->runtime.geometry_set_eval = NULL; - } -} - -void BKE_object_free_caches(Object *object) -{ - short update_flag = 0; - - /* Free particle system caches holding paths. */ - if (object->particlesystem.first) { - ParticleSystem *psys; - for (psys = object->particlesystem.first; psys != NULL; psys = psys->next) { - psys_free_path_cache(psys, psys->edit); - update_flag |= ID_RECALC_PSYS_REDO; - } - } - - /* Free memory used by cached derived meshes in the particle system modifiers. */ - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_ParticleSystem) { - ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - if (psmd->mesh_final) { - BKE_id_free(NULL, psmd->mesh_final); - psmd->mesh_final = NULL; - if (psmd->mesh_original) { - BKE_id_free(NULL, psmd->mesh_original); - psmd->mesh_original = NULL; - } - psmd->flag |= eParticleSystemFlag_file_loaded; - update_flag |= ID_RECALC_GEOMETRY; - } - } - } - - /* NOTE: If object is coming from a duplicator, it might be a temporary - * object created by dependency graph, which shares pointers with original - * object. In this case we can not free anything. - */ - if ((object->base_flag & BASE_FROM_DUPLI) == 0) { - BKE_object_free_derived_caches(object); - update_flag |= ID_RECALC_GEOMETRY; - } - - /* Tag object for update, so once memory critical operation is over and - * scene update routines are back to its business the object will be - * guaranteed to be in a known state. - */ - if (update_flag != 0) { - DEG_id_tag_update(&object->id, update_flag); - } -} - -/** - * Actual check for internal data, not context or flags. - */ -bool BKE_object_is_in_editmode(const Object *ob) -{ - if (ob->data == NULL) { - return false; - } - - switch (ob->type) { - case OB_MESH: - return ((Mesh *)ob->data)->edit_mesh != NULL; - case OB_ARMATURE: - return ((bArmature *)ob->data)->edbo != NULL; - case OB_FONT: - return ((Curve *)ob->data)->editfont != NULL; - case OB_MBALL: - return ((MetaBall *)ob->data)->editelems != NULL; - case OB_LATTICE: - return ((Lattice *)ob->data)->editlatt != NULL; - case OB_SURF: - case OB_CURVE: - return ((Curve *)ob->data)->editnurb != NULL; - case OB_GPENCIL: - /* Grease Pencil object has no edit mode data. */ - return GPENCIL_EDIT_MODE((bGPdata *)ob->data); - default: - return false; - } -} - -bool BKE_object_is_in_editmode_vgroup(const Object *ob) -{ - return (OB_TYPE_SUPPORT_VGROUP(ob->type) && BKE_object_is_in_editmode(ob)); -} - -bool BKE_object_data_is_in_editmode(const ID *id) -{ - const short type = GS(id->name); - BLI_assert(OB_DATA_SUPPORT_EDITMODE(type)); - switch (type) { - case ID_ME: - return ((const Mesh *)id)->edit_mesh != NULL; - case ID_CU: - return ((((const Curve *)id)->editnurb != NULL) || (((const Curve *)id)->editfont != NULL)); - case ID_MB: - return ((const MetaBall *)id)->editelems != NULL; - case ID_LT: - return ((const Lattice *)id)->editlatt != NULL; - case ID_AR: - return ((const bArmature *)id)->edbo != NULL; - default: - BLI_assert_unreachable(); - return false; - } -} - -char *BKE_object_data_editmode_flush_ptr_get(struct ID *id) -{ - const short type = GS(id->name); - switch (type) { - case ID_ME: { - BMEditMesh *em = ((Mesh *)id)->edit_mesh; - if (em != NULL) { - return &em->needs_flush_to_id; - } - break; - } - case ID_CU: { - if (((Curve *)id)->vfont != NULL) { - EditFont *ef = ((Curve *)id)->editfont; - if (ef != NULL) { - return &ef->needs_flush_to_id; - } - } - else { - EditNurb *editnurb = ((Curve *)id)->editnurb; - if (editnurb) { - return &editnurb->needs_flush_to_id; - } - } - break; - } - case ID_MB: { - MetaBall *mb = (MetaBall *)id; - return &mb->needs_flush_to_id; - } - case ID_LT: { - EditLatt *editlatt = ((Lattice *)id)->editlatt; - if (editlatt) { - return &editlatt->needs_flush_to_id; - } - break; - } - case ID_AR: { - bArmature *arm = (bArmature *)id; - return &arm->needs_flush_to_id; - } - default: - BLI_assert_unreachable(); - return NULL; - } - return NULL; -} - -bool BKE_object_is_in_wpaint_select_vert(const Object *ob) -{ - if (ob->type == OB_MESH) { - Mesh *me = ob->data; - return ((ob->mode & OB_MODE_WEIGHT_PAINT) && (me->edit_mesh == NULL) && - (ME_EDIT_PAINT_SEL_MODE(me) == SCE_SELECT_VERTEX)); - } - - return false; -} - -bool BKE_object_has_mode_data(const struct Object *ob, eObjectMode object_mode) -{ - if (object_mode & OB_MODE_EDIT) { - if (BKE_object_is_in_editmode(ob)) { - return true; - } - } - else if (object_mode & OB_MODE_VERTEX_PAINT) { - if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT)) { - return true; - } - } - else if (object_mode & OB_MODE_WEIGHT_PAINT) { - if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT)) { - return true; - } - } - else if (object_mode & OB_MODE_SCULPT) { - if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) { - return true; - } - } - else if (object_mode & OB_MODE_POSE) { - if (ob->pose != NULL) { - return true; - } - } - return false; -} - -bool BKE_object_is_mode_compat(const struct Object *ob, eObjectMode object_mode) -{ - return ((ob->mode == object_mode) || (ob->mode & object_mode) != 0); -} - -/** - * Return which parts of the object are visible, as evaluated by depsgraph - */ -int BKE_object_visibility(const Object *ob, const int dag_eval_mode) -{ - if ((ob->base_flag & BASE_VISIBLE_DEPSGRAPH) == 0) { - return 0; - } - - /* Test which components the object has. */ - int visibility = OB_VISIBLE_SELF; - if (ob->particlesystem.first) { - visibility |= OB_VISIBLE_INSTANCES | OB_VISIBLE_PARTICLES; - } - else if (ob->transflag & OB_DUPLI) { - visibility |= OB_VISIBLE_INSTANCES; - } - - if (BKE_object_has_geometry_set_instances(ob)) { - visibility |= OB_VISIBLE_INSTANCES; - } - - /* Optional hiding of self if there are particles or instancers. */ - if (visibility & (OB_VISIBLE_PARTICLES | OB_VISIBLE_INSTANCES)) { - switch ((eEvaluationMode)dag_eval_mode) { - case DAG_EVAL_VIEWPORT: - if (!(ob->duplicator_visibility_flag & OB_DUPLI_FLAG_VIEWPORT)) { - visibility &= ~OB_VISIBLE_SELF; - } - break; - case DAG_EVAL_RENDER: - if (!(ob->duplicator_visibility_flag & OB_DUPLI_FLAG_RENDER)) { - visibility &= ~OB_VISIBLE_SELF; - } - break; - } - } - - return visibility; -} - -bool BKE_object_exists_check(Main *bmain, const Object *obtest) -{ - if (obtest == NULL) { - return false; - } - - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob == obtest) { - return true; - } - } - - return false; -} - -/* *************************************************** */ - -static const char *get_obdata_defname(int type) -{ - switch (type) { - case OB_MESH: - return DATA_("Mesh"); - case OB_CURVE: - return DATA_("Curve"); - case OB_SURF: - return DATA_("Surf"); - case OB_FONT: - return DATA_("Text"); - case OB_MBALL: - return DATA_("Mball"); - case OB_CAMERA: - return DATA_("Camera"); - case OB_LAMP: - return CTX_DATA_(BLT_I18NCONTEXT_ID_LIGHT, "Light"); - case OB_LATTICE: - return DATA_("Lattice"); - case OB_ARMATURE: - return DATA_("Armature"); - case OB_SPEAKER: - return DATA_("Speaker"); - case OB_HAIR: - return DATA_("Hair"); - case OB_POINTCLOUD: - return DATA_("PointCloud"); - case OB_VOLUME: - return DATA_("Volume"); - case OB_EMPTY: - return DATA_("Empty"); - case OB_GPENCIL: - return DATA_("GPencil"); - case OB_LIGHTPROBE: - return DATA_("LightProbe"); - default: - CLOG_ERROR(&LOG, "Internal error, bad type: %d", type); - return DATA_("Empty"); - } -} - -static void object_init(Object *ob, const short ob_type) -{ - object_init_data(&ob->id); - - ob->type = ob_type; - - if (ob->type != OB_EMPTY) { - zero_v2(ob->ima_ofs); - } - - if (ELEM(ob->type, OB_LAMP, OB_CAMERA, OB_SPEAKER)) { - ob->trackflag = OB_NEGZ; - ob->upflag = OB_POSY; - } - - if (ob->type == OB_GPENCIL) { - ob->dtx |= OB_USE_GPENCIL_LIGHTS; - } - - if (ob->type == OB_LAMP) { - /* Lights are invisible to camera rays and are assumed to be a - * shadow catcher by default. */ - ob->visibility_flag |= OB_HIDE_CAMERA | OB_SHADOW_CATCHER; - } -} - -void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) -{ - if (name == NULL) { - name = get_obdata_defname(type); - } - - switch (type) { - case OB_MESH: - return BKE_mesh_add(bmain, name); - case OB_CURVE: - return BKE_curve_add(bmain, name, OB_CURVE); - case OB_SURF: - return BKE_curve_add(bmain, name, OB_SURF); - case OB_FONT: - return BKE_curve_add(bmain, name, OB_FONT); - case OB_MBALL: - return BKE_mball_add(bmain, name); - case OB_CAMERA: - return BKE_camera_add(bmain, name); - case OB_LAMP: - return BKE_light_add(bmain, name); - case OB_LATTICE: - return BKE_lattice_add(bmain, name); - case OB_ARMATURE: - return BKE_armature_add(bmain, name); - case OB_SPEAKER: - return BKE_speaker_add(bmain, name); - case OB_LIGHTPROBE: - return BKE_lightprobe_add(bmain, name); - case OB_GPENCIL: - return BKE_gpencil_data_addnew(bmain, name); - case OB_HAIR: - return BKE_hair_add(bmain, name); - case OB_POINTCLOUD: - return BKE_pointcloud_add_default(bmain, name); - case OB_VOLUME: - return BKE_volume_add(bmain, name); - case OB_EMPTY: - return NULL; - default: - CLOG_ERROR(&LOG, "Internal error, bad type: %d", type); - return NULL; - } -} - -/** - * Return -1 on failure. - */ -int BKE_object_obdata_to_type(const ID *id) -{ - /* Keep in sync with #OB_DATA_SUPPORT_ID macro. */ - switch (GS(id->name)) { - case ID_ME: - return OB_MESH; - case ID_CU: - return BKE_curve_type_get((const Curve *)id); - case ID_MB: - return OB_MBALL; - case ID_LA: - return OB_LAMP; - case ID_SPK: - return OB_SPEAKER; - case ID_CA: - return OB_CAMERA; - case ID_LT: - return OB_LATTICE; - case ID_GD: - return OB_GPENCIL; - case ID_AR: - return OB_ARMATURE; - case ID_LP: - return OB_LIGHTPROBE; - case ID_HA: - return OB_HAIR; - case ID_PT: - return OB_POINTCLOUD; - case ID_VO: - return OB_VOLUME; - default: - return -1; - } -} - -/** - * More general add: creates minimum required data, but without vertices etc. - */ -Object *BKE_object_add_only_object(Main *bmain, int type, const char *name) -{ - if (!name) { - name = get_obdata_defname(type); - } - - /* We cannot use #BKE_id_new here as we need some custom initialization code. */ - Object *ob = BKE_libblock_alloc(bmain, ID_OB, name, 0); - - /* We increase object user count when linking to Collections. */ - id_us_min(&ob->id); - - /* default object vars */ - object_init(ob, type); - - return ob; -} - -static Object *object_add_common(Main *bmain, ViewLayer *view_layer, int type, const char *name) -{ - Object *ob = BKE_object_add_only_object(bmain, type, name); - ob->data = BKE_object_obdata_add_from_type(bmain, type, name); - BKE_view_layer_base_deselect_all(view_layer); - - DEG_id_tag_update_ex( - bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - return ob; -} - -/** - * General add: to scene, with layer from area and default name - * - * Object is added to the active #Collection. - * If there is no linked collection to the active #ViewLayer we create a new one. - * - * \note Creates minimum required data, but without vertices etc. - */ -Object *BKE_object_add(Main *bmain, ViewLayer *view_layer, int type, const char *name) -{ - Object *ob = object_add_common(bmain, view_layer, type, name); - - LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, ob); - - Base *base = BKE_view_layer_base_find(view_layer, ob); - BKE_view_layer_base_select_and_set_active(view_layer, base); - - return ob; -} - -/** - * Add a new object, using another one as a reference - * - * \param ob_src: object to use to determine the collections of the new object. - */ -Object *BKE_object_add_from( - Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name, Object *ob_src) -{ - Object *ob = object_add_common(bmain, view_layer, type, name); - BKE_collection_object_add_from(bmain, scene, ob_src, ob); - - Base *base = BKE_view_layer_base_find(view_layer, ob); - BKE_view_layer_base_select_and_set_active(view_layer, base); - - return ob; -} - -/** - * Add a new object, but assign the given datablock as the ob->data - * for the newly created object. - * - * \param data: The datablock to assign as ob->data for the new object. - * This is assumed to be of the correct type. - * \param do_id_user: If true, id_us_plus() will be called on data when - * assigning it to the object. - */ -Object *BKE_object_add_for_data( - Main *bmain, ViewLayer *view_layer, int type, const char *name, ID *data, bool do_id_user) -{ - /* same as object_add_common, except we don't create new ob->data */ - Object *ob = BKE_object_add_only_object(bmain, type, name); - ob->data = data; - if (do_id_user) { - id_us_plus(data); - } - - BKE_view_layer_base_deselect_all(view_layer); - DEG_id_tag_update_ex( - bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - - LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, ob); - - Base *base = BKE_view_layer_base_find(view_layer, ob); - BKE_view_layer_base_select_and_set_active(view_layer, base); - - return ob; -} - -void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int flag) -{ - SoftBody *sb = ob_src->soft; - const bool is_orig = (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0; - - ob_dst->softflag = ob_src->softflag; - if (sb == NULL) { - ob_dst->soft = NULL; - return; - } - - SoftBody *sbn = MEM_dupallocN(sb); - - if ((flag & LIB_ID_COPY_CACHES) == 0) { - sbn->totspring = sbn->totpoint = 0; - sbn->bpoint = NULL; - sbn->bspring = NULL; - } - else { - sbn->totspring = sb->totspring; - sbn->totpoint = sb->totpoint; - - if (sbn->bpoint) { - int i; - - sbn->bpoint = MEM_dupallocN(sbn->bpoint); - - for (i = 0; i < sbn->totpoint; i++) { - if (sbn->bpoint[i].springs) { - sbn->bpoint[i].springs = MEM_dupallocN(sbn->bpoint[i].springs); - } - } - } - - if (sb->bspring) { - sbn->bspring = MEM_dupallocN(sb->bspring); - } - } - - sbn->keys = NULL; - sbn->totkey = sbn->totpointkey = 0; - - sbn->scratch = NULL; - - if (is_orig) { - sbn->shared = MEM_dupallocN(sb->shared); - sbn->shared->pointcache = BKE_ptcache_copy_list( - &sbn->shared->ptcaches, &sb->shared->ptcaches, flag); - } - - if (sb->effector_weights) { - sbn->effector_weights = MEM_dupallocN(sb->effector_weights); - } - - ob_dst->soft = sbn; -} - -ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int flag) -{ - ParticleSystem *psysn = MEM_dupallocN(psys); - - psys_copy_particles(psysn, psys); - - if (psys->clmd) { - psysn->clmd = (ClothModifierData *)BKE_modifier_new(eModifierType_Cloth); - BKE_modifier_copydata_ex((ModifierData *)psys->clmd, (ModifierData *)psysn->clmd, flag); - psys->hair_in_mesh = psys->hair_out_mesh = NULL; - } - - BLI_duplicatelist(&psysn->targets, &psys->targets); - - psysn->pathcache = NULL; - psysn->childcache = NULL; - psysn->edit = NULL; - psysn->pdd = NULL; - psysn->effectors = NULL; - psysn->tree = NULL; - psysn->bvhtree = NULL; - psysn->batch_cache = NULL; - - BLI_listbase_clear(&psysn->pathcachebufs); - BLI_listbase_clear(&psysn->childcachebufs); - - if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { - /* XXX Disabled, fails when evaluating depsgraph after copying ID with no main for preview - * creation. */ - // BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0); - psysn->flag |= PSYS_SHARED_CACHES; - BLI_assert(psysn->pointcache != NULL); - } - else { - psysn->pointcache = BKE_ptcache_copy_list(&psysn->ptcaches, &psys->ptcaches, flag); - } - - /* XXX(@campbellbarton): from reading existing code this seems correct but intended usage of - * point-cache should /w cloth should be added in 'ParticleSystem'. */ - if (psysn->clmd) { - psysn->clmd->point_cache = psysn->pointcache; - } - - if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { - id_us_plus((ID *)psysn->part); - } - - return psysn; -} - -void BKE_object_copy_particlesystems(Object *ob_dst, const Object *ob_src, const int flag) -{ - if (ob_dst->type != OB_MESH) { - /* currently only mesh objects can have soft body */ - return; - } - - BLI_listbase_clear(&ob_dst->particlesystem); - LISTBASE_FOREACH (ParticleSystem *, psys, &ob_src->particlesystem) { - ParticleSystem *npsys = BKE_object_copy_particlesystem(psys, flag); - - BLI_addtail(&ob_dst->particlesystem, npsys); - - /* need to update particle modifiers too */ - LISTBASE_FOREACH (ModifierData *, md, &ob_dst->modifiers) { - if (md->type == eModifierType_ParticleSystem) { - ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - if (psmd->psys == psys) { - psmd->psys = npsys; - } - } - else if (md->type == eModifierType_DynamicPaint) { - DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; - if (pmd->brush) { - if (pmd->brush->psys == psys) { - pmd->brush->psys = npsys; - } - } - } - else if (md->type == eModifierType_Fluid) { - FluidModifierData *fmd = (FluidModifierData *)md; - - if (fmd->type == MOD_FLUID_TYPE_FLOW) { - if (fmd->flow) { - if (fmd->flow->psys == psys) { - fmd->flow->psys = npsys; - } - } - } - } - } - } -} - -static void copy_object_pose(Object *obn, const Object *ob, const int flag) -{ - /* NOTE: need to clear obn->pose pointer first, - * so that BKE_pose_copy_data works (otherwise there's a crash) */ - obn->pose = NULL; - BKE_pose_copy_data_ex(&obn->pose, ob->pose, flag, true); /* true = copy constraints */ - - LISTBASE_FOREACH (bPoseChannel *, chan, &obn->pose->chanbase) { - chan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE); - - /* XXX Remapping object pointing onto itself should be handled by generic - * BKE_library_remap stuff, but... - * the flush_constraint_targets callback am not sure about, so will delay that for now. */ - LISTBASE_FOREACH (bConstraint *, con, &chan->constraints) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == ob) { - ct->tar = obn; - } - } - - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - } -} - -bool BKE_object_pose_context_check(const Object *ob) -{ - if ((ob) && (ob->type == OB_ARMATURE) && (ob->pose) && (ob->mode & OB_MODE_POSE)) { - return true; - } - - return false; -} - -Object *BKE_object_pose_armature_get(Object *ob) -{ - if (ob == NULL) { - return NULL; - } - - if (BKE_object_pose_context_check(ob)) { - return ob; - } - - ob = BKE_modifiers_is_deformed_by_armature(ob); - - /* Only use selected check when non-active. */ - if (BKE_object_pose_context_check(ob)) { - return ob; - } - - return NULL; -} - -Object *BKE_object_pose_armature_get_visible(Object *ob, ViewLayer *view_layer, View3D *v3d) -{ - Object *ob_armature = BKE_object_pose_armature_get(ob); - if (ob_armature) { - Base *base = BKE_view_layer_base_find(view_layer, ob_armature); - if (base) { - if (BASE_VISIBLE(v3d, base)) { - return ob_armature; - } - } - } - return NULL; -} - -/** - * Access pose array with special check to get pose object when in weight paint mode. - */ -Object **BKE_object_pose_array_get_ex(ViewLayer *view_layer, - View3D *v3d, - uint *r_objects_len, - bool unique) -{ - Object *ob_active = OBACT(view_layer); - Object *ob_pose = BKE_object_pose_armature_get(ob_active); - Object **objects = NULL; - if (ob_pose == ob_active) { - objects = BKE_view_layer_array_from_objects_in_mode(view_layer, - v3d, - r_objects_len, - { - .object_mode = OB_MODE_POSE, - .no_dup_data = unique, - }); - } - else if (ob_pose != NULL) { - *r_objects_len = 1; - objects = MEM_mallocN(sizeof(*objects), __func__); - objects[0] = ob_pose; - } - else { - *r_objects_len = 0; - objects = MEM_mallocN(0, __func__); - } - return objects; -} -Object **BKE_object_pose_array_get_unique(ViewLayer *view_layer, View3D *v3d, uint *r_objects_len) -{ - return BKE_object_pose_array_get_ex(view_layer, v3d, r_objects_len, true); -} -Object **BKE_object_pose_array_get(ViewLayer *view_layer, View3D *v3d, uint *r_objects_len) -{ - return BKE_object_pose_array_get_ex(view_layer, v3d, r_objects_len, false); -} - -Base **BKE_object_pose_base_array_get_ex(ViewLayer *view_layer, - View3D *v3d, - uint *r_bases_len, - bool unique) -{ - Base *base_active = BASACT(view_layer); - Object *ob_pose = base_active ? BKE_object_pose_armature_get(base_active->object) : NULL; - Base *base_pose = NULL; - Base **bases = NULL; - - if (base_active) { - if (ob_pose == base_active->object) { - base_pose = base_active; - } - else { - base_pose = BKE_view_layer_base_find(view_layer, ob_pose); - } - } - - if (base_active && (base_pose == base_active)) { - bases = BKE_view_layer_array_from_bases_in_mode(view_layer, - v3d, - r_bases_len, - { - .object_mode = OB_MODE_POSE, - .no_dup_data = unique, - }); - } - else if (base_pose != NULL) { - *r_bases_len = 1; - bases = MEM_mallocN(sizeof(*bases), __func__); - bases[0] = base_pose; - } - else { - *r_bases_len = 0; - bases = MEM_mallocN(0, __func__); - } - return bases; -} -Base **BKE_object_pose_base_array_get_unique(ViewLayer *view_layer, View3D *v3d, uint *r_bases_len) -{ - return BKE_object_pose_base_array_get_ex(view_layer, v3d, r_bases_len, true); -} -Base **BKE_object_pose_base_array_get(ViewLayer *view_layer, View3D *v3d, uint *r_bases_len) -{ - return BKE_object_pose_base_array_get_ex(view_layer, v3d, r_bases_len, false); -} - -void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src) -{ - copy_v3_v3(ob_tar->loc, ob_src->loc); - copy_v3_v3(ob_tar->rot, ob_src->rot); - copy_v4_v4(ob_tar->quat, ob_src->quat); - copy_v3_v3(ob_tar->rotAxis, ob_src->rotAxis); - ob_tar->rotAngle = ob_src->rotAngle; - ob_tar->rotmode = ob_src->rotmode; - copy_v3_v3(ob_tar->scale, ob_src->scale); -} - -/** - * Perform deep-copy of object and its 'children' data-blocks (obdata, materials, actions, etc.). - * - * \param dupflag: Controls which sub-data are also duplicated - * (see #eDupli_ID_Flags in DNA_userdef_types.h). - * - * \note This function does not do any remapping to new IDs, caller must do it - * (\a #BKE_libblock_relink_to_newid()). - * \note Caller MUST free \a newid pointers itself (#BKE_main_id_newptr_and_tag_clear()) and call - * updates of DEG too (#DAG_relations_tag_update()). - */ -Object *BKE_object_duplicate(Main *bmain, - Object *ob, - eDupli_ID_Flags dupflag, - eLibIDDuplicateFlags duplicate_options) -{ - const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; - const bool is_root_id = (duplicate_options & LIB_ID_DUPLICATE_IS_ROOT_ID) != 0; - int copy_flags = LIB_ID_COPY_DEFAULT; - - if (!is_subprocess) { - BKE_main_id_newptr_and_tag_clear(bmain); - } - else { - /* In case copying object is a sub-process of collection (or scene) copying, do not try to - * re-assign RB objects to existing RBW collections. */ - copy_flags |= LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING; - } - if (is_root_id) { - /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate - * all expected linked data. */ - if (ID_IS_LINKED(ob)) { - dupflag |= USER_DUP_LINKED_ID; - } - duplicate_options &= ~LIB_ID_DUPLICATE_IS_ROOT_ID; - } - - Material ***matarar; - - Object *obn = (Object *)BKE_id_copy_for_duplicate(bmain, &ob->id, dupflag, copy_flags); - - /* 0 == full linked. */ - if (dupflag == 0) { - return obn; - } - - if (dupflag & USER_DUP_MAT) { - for (int i = 0; i < obn->totcol; i++) { - BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag, copy_flags); - } - } - if (dupflag & USER_DUP_PSYS) { - ParticleSystem *psys; - for (psys = obn->particlesystem.first; psys; psys = psys->next) { - BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag, copy_flags); - } - } - - ID *id_old = obn->data; - ID *id_new = NULL; - const bool need_to_duplicate_obdata = (id_old != NULL) && (id_old->newid == NULL); - - switch (obn->type) { - case OB_MESH: - if (dupflag & USER_DUP_MESH) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_CURVE: - if (dupflag & USER_DUP_CURVE) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_SURF: - if (dupflag & USER_DUP_SURF) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_FONT: - if (dupflag & USER_DUP_FONT) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_MBALL: - if (dupflag & USER_DUP_MBALL) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_LAMP: - if (dupflag & USER_DUP_LAMP) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_ARMATURE: - if (dupflag & USER_DUP_ARM) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_LATTICE: - if (dupflag & USER_DUP_LATTICE) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_CAMERA: - if (dupflag & USER_DUP_CAMERA) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_LIGHTPROBE: - if (dupflag & USER_DUP_LIGHTPROBE) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_SPEAKER: - if (dupflag & USER_DUP_SPEAKER) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_GPENCIL: - if (dupflag & USER_DUP_GPENCIL) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_HAIR: - if (dupflag & USER_DUP_HAIR) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_POINTCLOUD: - if (dupflag & USER_DUP_POINTCLOUD) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - case OB_VOLUME: - if (dupflag & USER_DUP_VOLUME) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); - } - break; - } - - /* If obdata has been copied, we may also have to duplicate the materials assigned to it. */ - if (need_to_duplicate_obdata && !ELEM(id_new, NULL, id_old)) { - if (dupflag & USER_DUP_MAT) { - matarar = BKE_object_material_array_p(obn); - if (matarar) { - for (int i = 0; i < obn->totcol; i++) { - BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], dupflag, copy_flags); - } - } - } - } - - if (!is_subprocess) { - /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&obn->id); - -#ifndef NDEBUG - /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ - ID *id_iter; - FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0); - } - FOREACH_MAIN_ID_END; -#endif - - /* Cleanup. */ - BKE_main_id_newptr_and_tag_clear(bmain); - } - - if (obn->type == OB_ARMATURE) { - DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY); - if (obn->pose) { - BKE_pose_tag_recalc(bmain, obn->pose); - } - // BKE_pose_rebuild(bmain, obn, obn->data, true); - } - - if (obn->data != NULL) { - DEG_id_tag_update_ex(bmain, (ID *)obn->data, ID_RECALC_EDITORS); - } - - return obn; -} - -/** - * Returns true if the Object is from an external blend file (libdata). - */ -bool BKE_object_is_libdata(const Object *ob) -{ - return (ob && ID_IS_LINKED(ob)); -} - -/** - * Returns true if the Object data is from an external blend file (libdata). - */ -bool BKE_object_obdata_is_libdata(const Object *ob) -{ - /* Linked objects with local obdata are forbidden! */ - BLI_assert(!ob || !ob->data || (ID_IS_LINKED(ob) ? ID_IS_LINKED(ob->data) : true)); - return (ob && ob->data && ID_IS_LINKED(ob->data)); -} - -/* -------------------------------------------------------------------- */ -/** \name Object Proxy API - * \{ */ - -/* when you make proxy, ensure the exposed layers are extern */ -static void armature_set_id_extern(Object *ob) -{ - bArmature *arm = ob->data; - bPoseChannel *pchan; - unsigned int lay = arm->layer_protected; - - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (!(pchan->bone->layer & lay)) { - id_lib_extern((ID *)pchan->custom); - } - } -} - -void BKE_object_copy_proxy_drivers(Object *ob, Object *target) -{ - if ((target->adt) && (target->adt->drivers.first)) { - FCurve *fcu; - - /* add new animdata block */ - if (!ob->adt) { - ob->adt = BKE_animdata_ensure_id(&ob->id); - } - - /* make a copy of all the drivers (for now), then correct any links that need fixing */ - BKE_fcurves_free(&ob->adt->drivers); - BKE_fcurves_copy(&ob->adt->drivers, &target->adt->drivers); - - for (fcu = ob->adt->drivers.first; fcu; fcu = fcu->next) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - /* all drivers */ - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - if (dtar->id) { - if ((Object *)dtar->id == target) { - dtar->id = (ID *)ob; - } - else { - /* only on local objects because this causes indirect links - * 'a -> b -> c', blend to point directly to a.blend - * when a.blend has a proxy that's linked into `c.blend`. */ - if (!ID_IS_LINKED(ob)) { - id_lib_extern((ID *)dtar->id); - } - } - } - } - DRIVER_TARGETS_LOOPER_END; - } - } - } -} - -/** - * Proxy rule: - * - lib_object->proxy_from == the one we borrow from, set temporally while object_update. - * - local_object->proxy == pointer to library object, saved in files and read. - * - local_object->proxy_group == pointer to collection dupli-object, saved in files and read. - */ -void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob) -{ - /* paranoia checks */ - if (ID_IS_LINKED(ob) || !ID_IS_LINKED(target)) { - CLOG_ERROR(&LOG, "cannot make proxy"); - return; - } - - ob->proxy = target; - id_us_plus(&target->id); - ob->proxy_group = cob; - - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - DEG_id_tag_update(&target->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - - /* copy transform - * - cob means this proxy comes from a collection, just apply the matrix - * so the object won't move from its dupli-transform. - * - * - no cob means this is being made from a linked object, - * this is closer to making a copy of the object - in-place. */ - if (cob) { - ob->rotmode = target->rotmode; - mul_m4_m4m4(ob->obmat, cob->obmat, target->obmat); - if (cob->instance_collection) { /* should always be true */ - float tvec[3]; - mul_v3_mat3_m4v3(tvec, ob->obmat, cob->instance_collection->instance_offset); - sub_v3_v3(ob->obmat[3], tvec); - } - BKE_object_apply_mat4(ob, ob->obmat, false, true); - } - else { - BKE_object_transform_copy(ob, target); - ob->parent = target->parent; /* libdata */ - copy_m4_m4(ob->parentinv, target->parentinv); - } - - /* copy animdata stuff - drivers only for now... */ - BKE_object_copy_proxy_drivers(ob, target); - - /* skip constraints? */ - /* FIXME: this is considered by many as a bug */ - - /* set object type and link to data */ - ob->type = target->type; - ob->data = target->data; - id_us_plus((ID *)ob->data); /* ensures lib data becomes LIB_TAG_EXTERN */ - - /* copy material and index information */ - ob->actcol = ob->totcol = 0; - if (ob->mat) { - MEM_freeN(ob->mat); - } - if (ob->matbits) { - MEM_freeN(ob->matbits); - } - ob->mat = NULL; - ob->matbits = NULL; - if ((target->totcol) && (target->mat) && OB_TYPE_SUPPORT_MATERIAL(ob->type)) { - int i; - - ob->actcol = target->actcol; - ob->totcol = target->totcol; - - ob->mat = MEM_dupallocN(target->mat); - ob->matbits = MEM_dupallocN(target->matbits); - for (i = 0; i < target->totcol; i++) { - /* don't need to run BKE_object_materials_test - * since we know this object is new and not used elsewhere */ - id_us_plus((ID *)ob->mat[i]); - } - } - - /* type conversions */ - if (target->type == OB_ARMATURE) { - copy_object_pose(ob, target, 0); /* data copy, object pointers in constraints */ - BKE_pose_rest(ob->pose, false); /* clear all transforms in channels */ - BKE_pose_rebuild(bmain, ob, ob->data, true); /* set all internal links */ - - armature_set_id_extern(ob); - } - else if (target->type == OB_EMPTY) { - ob->empty_drawtype = target->empty_drawtype; - ob->empty_drawsize = target->empty_drawsize; - } - - /* copy IDProperties */ - if (ob->id.properties) { - IDP_FreeProperty(ob->id.properties); - ob->id.properties = NULL; - } - if (target->id.properties) { - ob->id.properties = IDP_CopyProperty(target->id.properties); - } - - /* copy drawtype info */ - ob->dt = target->dt; -} - -/** - * Use with newly created objects to set their size - * (used to apply scene-scale). - */ -void BKE_object_obdata_size_init(struct Object *ob, const float size) -{ - /* apply radius as a scale to types that support it */ - switch (ob->type) { - case OB_EMPTY: { - ob->empty_drawsize *= size; - break; - } - case OB_FONT: { - Curve *cu = ob->data; - cu->fsize *= size; - break; - } - case OB_CAMERA: { - Camera *cam = ob->data; - cam->drawsize *= size; - break; - } - case OB_LAMP: { - Light *lamp = ob->data; - lamp->dist *= size; - lamp->area_size *= size; - lamp->area_sizey *= size; - lamp->area_sizez *= size; - break; - } - /* Only lattice (not mesh, curve, mball...), - * because its got data when newly added */ - case OB_LATTICE: { - struct Lattice *lt = ob->data; - float mat[4][4]; - - unit_m4(mat); - scale_m4_fl(mat, size); - - BKE_lattice_transform(lt, (float(*)[4])mat, false); - break; - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Object Matrix Get/Set API - * \{ */ - -void BKE_object_scale_to_mat3(Object *ob, float mat[3][3]) -{ - float vec[3]; - mul_v3_v3v3(vec, ob->scale, ob->dscale); - size_to_mat3(mat, vec); -} - -void BKE_object_rot_to_mat3(const Object *ob, float mat[3][3], bool use_drot) -{ - float rmat[3][3], dmat[3][3]; - - /* 'dmat' is the delta-rotation matrix, which will get (pre)multiplied - * with the rotation matrix to yield the appropriate rotation - */ - - /* rotations may either be quats, eulers (with various rotation orders), or axis-angle */ - if (ob->rotmode > 0) { - /* Euler rotations - * (will cause gimbal lock, but this can be alleviated a bit with rotation orders). */ - eulO_to_mat3(rmat, ob->rot, ob->rotmode); - eulO_to_mat3(dmat, ob->drot, ob->rotmode); - } - else if (ob->rotmode == ROT_MODE_AXISANGLE) { - /* axis-angle - not really that great for 3D-changing orientations */ - axis_angle_to_mat3(rmat, ob->rotAxis, ob->rotAngle); - axis_angle_to_mat3(dmat, ob->drotAxis, ob->drotAngle); - } - else { - /* quats are normalized before use to eliminate scaling issues */ - float tquat[4]; - - normalize_qt_qt(tquat, ob->quat); - quat_to_mat3(rmat, tquat); - - normalize_qt_qt(tquat, ob->dquat); - quat_to_mat3(dmat, tquat); - } - - /* combine these rotations */ - if (use_drot) { - mul_m3_m3m3(mat, dmat, rmat); - } - else { - copy_m3_m3(mat, rmat); - } -} - -void BKE_object_mat3_to_rot(Object *ob, float mat[3][3], bool use_compat) -{ - BLI_ASSERT_UNIT_M3(mat); - - switch (ob->rotmode) { - case ROT_MODE_QUAT: { - float dquat[4]; - mat3_normalized_to_quat(ob->quat, mat); - normalize_qt_qt(dquat, ob->dquat); - invert_qt_normalized(dquat); - mul_qt_qtqt(ob->quat, dquat, ob->quat); - break; - } - case ROT_MODE_AXISANGLE: { - float quat[4]; - float dquat[4]; - - /* without drot we could apply 'mat' directly */ - mat3_normalized_to_quat(quat, mat); - axis_angle_to_quat(dquat, ob->drotAxis, ob->drotAngle); - invert_qt_normalized(dquat); - mul_qt_qtqt(quat, dquat, quat); - quat_to_axis_angle(ob->rotAxis, &ob->rotAngle, quat); - break; - } - default: /* euler */ - { - float quat[4]; - float dquat[4]; - - /* without drot we could apply 'mat' directly */ - mat3_normalized_to_quat(quat, mat); - eulO_to_quat(dquat, ob->drot, ob->rotmode); - invert_qt_normalized(dquat); - mul_qt_qtqt(quat, dquat, quat); - /* end drot correction */ - - if (use_compat) { - quat_to_compatible_eulO(ob->rot, ob->rot, ob->rotmode, quat); - } - else { - quat_to_eulO(ob->rot, ob->rotmode, quat); - } - break; - } - } -} - -void BKE_object_tfm_protected_backup(const Object *ob, ObjectTfmProtectedChannels *obtfm) -{ - -#define TFMCPY(_v) (obtfm->_v = ob->_v) -#define TFMCPY3D(_v) copy_v3_v3(obtfm->_v, ob->_v) -#define TFMCPY4D(_v) copy_v4_v4(obtfm->_v, ob->_v) - - TFMCPY3D(loc); - TFMCPY3D(dloc); - TFMCPY3D(scale); - TFMCPY3D(dscale); - TFMCPY3D(rot); - TFMCPY3D(drot); - TFMCPY4D(quat); - TFMCPY4D(dquat); - TFMCPY3D(rotAxis); - TFMCPY3D(drotAxis); - TFMCPY(rotAngle); - TFMCPY(drotAngle); - -#undef TFMCPY -#undef TFMCPY3D -#undef TFMCPY4D -} - -void BKE_object_tfm_protected_restore(Object *ob, - const ObjectTfmProtectedChannels *obtfm, - const short protectflag) -{ - unsigned int i; - - for (i = 0; i < 3; i++) { - if (protectflag & (OB_LOCK_LOCX << i)) { - ob->loc[i] = obtfm->loc[i]; - ob->dloc[i] = obtfm->dloc[i]; - } - - if (protectflag & (OB_LOCK_SCALEX << i)) { - ob->scale[i] = obtfm->scale[i]; - ob->dscale[i] = obtfm->dscale[i]; - } - - if (protectflag & (OB_LOCK_ROTX << i)) { - ob->rot[i] = obtfm->rot[i]; - ob->drot[i] = obtfm->drot[i]; - - ob->quat[i + 1] = obtfm->quat[i + 1]; - ob->dquat[i + 1] = obtfm->dquat[i + 1]; - - ob->rotAxis[i] = obtfm->rotAxis[i]; - ob->drotAxis[i] = obtfm->drotAxis[i]; - } - } - - if ((protectflag & OB_LOCK_ROT4D) && (protectflag & OB_LOCK_ROTW)) { - ob->quat[0] = obtfm->quat[0]; - ob->dquat[0] = obtfm->dquat[0]; - - ob->rotAngle = obtfm->rotAngle; - ob->drotAngle = obtfm->drotAngle; - } -} - -void BKE_object_tfm_copy(Object *object_dst, const Object *object_src) -{ -#define TFMCPY(_v) (object_dst->_v = object_src->_v) -#define TFMCPY3D(_v) copy_v3_v3(object_dst->_v, object_src->_v) -#define TFMCPY4D(_v) copy_v4_v4(object_dst->_v, object_src->_v) - - TFMCPY3D(loc); - TFMCPY3D(dloc); - TFMCPY3D(scale); - TFMCPY3D(dscale); - TFMCPY3D(rot); - TFMCPY3D(drot); - TFMCPY4D(quat); - TFMCPY4D(dquat); - TFMCPY3D(rotAxis); - TFMCPY3D(drotAxis); - TFMCPY(rotAngle); - TFMCPY(drotAngle); - -#undef TFMCPY -#undef TFMCPY3D -#undef TFMCPY4D -} - -void BKE_object_to_mat3(Object *ob, float r_mat[3][3]) /* no parent */ -{ - float smat[3][3]; - float rmat[3][3]; - - /* Scale. */ - BKE_object_scale_to_mat3(ob, smat); - - /* Rotation. */ - BKE_object_rot_to_mat3(ob, rmat, true); - mul_m3_m3m3(r_mat, rmat, smat); -} - -void BKE_object_to_mat4(Object *ob, float r_mat[4][4]) -{ - float tmat[3][3]; - - BKE_object_to_mat3(ob, tmat); - - copy_m4_m3(r_mat, tmat); - - add_v3_v3v3(r_mat[3], ob->loc, ob->dloc); -} - -void BKE_object_matrix_local_get(struct Object *ob, float r_mat[4][4]) -{ - if (ob->parent) { - float par_imat[4][4]; - - BKE_object_get_parent_matrix(ob, ob->parent, par_imat); - invert_m4(par_imat); - mul_m4_m4m4(r_mat, par_imat, ob->obmat); - } - else { - copy_m4_m4(r_mat, ob->obmat); - } -} - -/** - * \return success if \a mat is set. - */ -static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4]) -{ - Curve *cu = par->data; - float vec[4], dir[3], quat[4], radius, ctime; - - /* NOTE: Curve cache is supposed to be evaluated here already, however there - * are cases where we can not guarantee that. This includes, for example, - * dependency cycles. We can't correct anything from here, since that would - * cause a threading conflicts. - * - * TODO(sergey): Some of the legit looking cases like T56619 need to be - * looked into, and maybe curve cache (and other dependencies) are to be - * evaluated prior to conversion. */ - if (par->runtime.curve_cache == NULL) { - return false; - } - if (par->runtime.curve_cache->anim_path_accum_length == NULL) { - return false; - } - - /* ctime is now a proper var setting of Curve which gets set by Animato like any other var - * that's animated, but this will only work if it actually is animated. - * - * We divide the curve-time calculated in the previous step by the length of the path, - * to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. - */ - if (cu->pathlen) { - ctime = cu->ctime / cu->pathlen; - } - else { - ctime = cu->ctime; - } - - if (cu->flag & CU_PATH_CLAMP) { - CLAMP(ctime, 0.0f, 1.0f); - } - - unit_m4(r_mat); - - /* vec: 4 items! */ - if (BKE_where_on_path( - par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : NULL, &radius, NULL)) { - if (cu->flag & CU_FOLLOW) { - quat_apply_track(quat, ob->trackflag, ob->upflag); - normalize_qt(quat); - quat_to_mat4(r_mat, quat); - } - if (cu->flag & CU_PATH_RADIUS) { - float tmat[4][4], rmat[4][4]; - scale_m4_fl(tmat, radius); - mul_m4_m4m4(rmat, tmat, r_mat); - copy_m4_m4(r_mat, rmat); - } - copy_v3_v3(r_mat[3], vec); - } - - return true; -} - -static void ob_parbone(Object *ob, Object *par, float r_mat[4][4]) -{ - float vec[3]; - - if (par->type != OB_ARMATURE) { - unit_m4(r_mat); - return; - } - - /* Make sure the bone is still valid */ - bPoseChannel *pchan = BKE_pose_channel_find_name(par->pose, ob->parsubstr); - if (!pchan || !pchan->bone) { - CLOG_WARN( - &LOG, "Parent Bone: '%s' for Object: '%s' doesn't exist", ob->parsubstr, ob->id.name + 2); - unit_m4(r_mat); - return; - } - - /* get bone transform */ - if (pchan->bone->flag & BONE_RELATIVE_PARENTING) { - /* the new option uses the root - expected behavior, but differs from old... */ - /* XXX check on version patching? */ - copy_m4_m4(r_mat, pchan->chan_mat); - } - else { - copy_m4_m4(r_mat, pchan->pose_mat); - - /* but for backwards compatibility, the child has to move to the tail */ - copy_v3_v3(vec, r_mat[1]); - mul_v3_fl(vec, pchan->bone->length); - add_v3_v3(r_mat[3], vec); - } -} - -static void give_parvert(Object *par, int nr, float vec[3]) -{ - zero_v3(vec); - - if (par->type == OB_MESH) { - Mesh *me = par->data; - BMEditMesh *em = me->edit_mesh; - Mesh *me_eval = (em) ? em->mesh_eval_final : BKE_object_get_evaluated_mesh(par); - - if (me_eval) { - int count = 0; - int numVerts = me_eval->totvert; - - if (em && me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { - numVerts = em->bm->totvert; - if (em->bm->elem_table_dirty & BM_VERT) { -#ifdef VPARENT_THREADING_HACK - BLI_mutex_lock(&vparent_lock); - if (em->bm->elem_table_dirty & BM_VERT) { - BM_mesh_elem_table_ensure(em->bm, BM_VERT); - } - BLI_mutex_unlock(&vparent_lock); -#else - BLI_assert_msg(0, "Not safe for threading"); - BM_mesh_elem_table_ensure(em->bm, BM_VERT); -#endif - } - if (nr < numVerts) { - if (me_eval && me_eval->runtime.edit_data && me_eval->runtime.edit_data->vertexCos) { - add_v3_v3(vec, me_eval->runtime.edit_data->vertexCos[nr]); - } - else { - const BMVert *v = BM_vert_at_index(em->bm, nr); - add_v3_v3(vec, v->co); - } - count++; - } - } - else if (CustomData_has_layer(&me_eval->vdata, CD_ORIGINDEX)) { - const int *index = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); - /* Get the average of all verts with (original index == nr). */ - for (int i = 0; i < numVerts; i++) { - if (index[i] == nr) { - add_v3_v3(vec, me_eval->mvert[i].co); - count++; - } - } - } - else { - if (nr < numVerts) { - add_v3_v3(vec, me_eval->mvert[nr].co); - count++; - } - } - - if (count == 0) { - /* keep as 0, 0, 0 */ - } - else if (count > 0) { - mul_v3_fl(vec, 1.0f / count); - } - else { - /* use first index if its out of range */ - if (me_eval->totvert) { - copy_v3_v3(vec, me_eval->mvert[0].co); - } - } - } - else { - CLOG_ERROR(&LOG, - "Evaluated mesh is needed to solve parenting, " - "object position can be wrong now"); - } - } - else if (ELEM(par->type, OB_CURVE, OB_SURF)) { - ListBase *nurb; - - /* Unless there's some weird depsgraph failure the cache should exist. */ - BLI_assert(par->runtime.curve_cache != NULL); - - if (par->runtime.curve_cache->deformed_nurbs.first != NULL) { - nurb = &par->runtime.curve_cache->deformed_nurbs; - } - else { - Curve *cu = par->data; - nurb = BKE_curve_nurbs_get(cu); - } - - BKE_nurbList_index_get_co(nurb, nr, vec); - } - else if (par->type == OB_LATTICE) { - Lattice *latt = par->data; - DispList *dl = par->runtime.curve_cache ? - BKE_displist_find(&par->runtime.curve_cache->disp, DL_VERTS) : - NULL; - float(*co)[3] = dl ? (float(*)[3])dl->verts : NULL; - int tot; - - if (latt->editlatt) { - latt = latt->editlatt->latt; - } - - tot = latt->pntsu * latt->pntsv * latt->pntsw; - - /* ensure dl is correct size */ - BLI_assert(dl == NULL || dl->nr == tot); - - if (nr < tot) { - if (co) { - copy_v3_v3(vec, co[nr]); - } - else { - copy_v3_v3(vec, latt->def[nr].vec); - } - } - } -} - -static void ob_parvert3(Object *ob, Object *par, float r_mat[4][4]) -{ - /* in local ob space */ - if (OB_TYPE_SUPPORT_PARVERT(par->type)) { - float cmat[3][3], v1[3], v2[3], v3[3], q[4]; - - give_parvert(par, ob->par1, v1); - give_parvert(par, ob->par2, v2); - give_parvert(par, ob->par3, v3); - - tri_to_quat(q, v1, v2, v3); - quat_to_mat3(cmat, q); - copy_m4_m3(r_mat, cmat); - - mid_v3_v3v3v3(r_mat[3], v1, v2, v3); - } - else { - unit_m4(r_mat); - } -} - -void BKE_object_get_parent_matrix(Object *ob, Object *par, float r_parentmat[4][4]) -{ - float tmat[4][4]; - float vec[3]; - - switch (ob->partype & PARTYPE) { - case PAROBJECT: { - bool ok = false; - if (par->type == OB_CURVE) { - if ((((Curve *)par->data)->flag & CU_PATH) && (ob_parcurve(ob, par, tmat))) { - ok = true; - } - } - - if (ok) { - mul_m4_m4m4(r_parentmat, par->obmat, tmat); - } - else { - copy_m4_m4(r_parentmat, par->obmat); - } - - break; - } - case PARBONE: - ob_parbone(ob, par, tmat); - mul_m4_m4m4(r_parentmat, par->obmat, tmat); - break; - - case PARVERT1: - unit_m4(r_parentmat); - give_parvert(par, ob->par1, vec); - mul_v3_m4v3(r_parentmat[3], par->obmat, vec); - break; - case PARVERT3: - ob_parvert3(ob, par, tmat); - - mul_m4_m4m4(r_parentmat, par->obmat, tmat); - break; - - case PARSKEL: - copy_m4_m4(r_parentmat, par->obmat); - break; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Object Matrix Evaluation API - * \{ */ - -/** - * \param r_originmat: Optional matrix that stores the space the object is in - * (without its own matrix applied) - */ -static void solve_parenting( - Object *ob, Object *par, const bool set_origin, float r_obmat[4][4], float r_originmat[3][3]) -{ - float totmat[4][4]; - float tmat[4][4]; - float locmat[4][4]; - - BKE_object_to_mat4(ob, locmat); - - BKE_object_get_parent_matrix(ob, par, totmat); - - /* total */ - mul_m4_m4m4(tmat, totmat, ob->parentinv); - mul_m4_m4m4(r_obmat, tmat, locmat); - - if (r_originmat) { - /* usable originmat */ - copy_m3_m4(r_originmat, tmat); - } - - /* origin, for help line */ - if (set_origin) { - if ((ob->partype & PARTYPE) == PARSKEL) { - copy_v3_v3(ob->runtime.parent_display_origin, par->obmat[3]); - } - else { - copy_v3_v3(ob->runtime.parent_display_origin, totmat[3]); - } - } -} - -static void object_where_is_calc_ex(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - float ctime, - RigidBodyWorld *rbw, - float r_originmat[3][3]) -{ - if (ob->parent) { - Object *par = ob->parent; - - /* calculate parent matrix */ - solve_parenting(ob, par, true, ob->obmat, r_originmat); - } - else { - BKE_object_to_mat4(ob, ob->obmat); - } - - /* try to fall back to the scene rigid body world if none given */ - rbw = rbw ? rbw : scene->rigidbody_world; - /* read values pushed into RBO from sim/cache... */ - BKE_rigidbody_sync_transforms(rbw, ob, ctime); - - /* solve constraints */ - if (ob->constraints.first && !(ob->transflag & OB_NO_CONSTRAINTS)) { - bConstraintOb *cob; - cob = BKE_constraints_make_evalob(depsgraph, scene, ob, NULL, CONSTRAINT_OBTYPE_OBJECT); - BKE_constraints_solve(depsgraph, &ob->constraints, cob, ctime); - BKE_constraints_clear_evalob(cob); - } - - /* set negative scale flag in object */ - if (is_negative_m4(ob->obmat)) { - ob->transflag |= OB_NEG_SCALE; - } - else { - ob->transflag &= ~OB_NEG_SCALE; - } -} - -void BKE_object_where_is_calc_time(Depsgraph *depsgraph, Scene *scene, Object *ob, float ctime) -{ - /* Execute drivers and animation. */ - const bool flush_to_original = DEG_is_active(depsgraph); - const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, - ctime); - BKE_animsys_evaluate_animdata( - &ob->id, ob->adt, &anim_eval_context, ADT_RECALC_ALL, flush_to_original); - object_where_is_calc_ex(depsgraph, scene, ob, ctime, NULL, NULL); -} - -/** - * Calculate object transformation matrix without recalculating dependencies and - * constraints -- assume dependencies are already solved by depsgraph. - * No changes to object and its parent would be done. - * Used for bundles orientation in 3d space relative to parented blender camera. - */ -void BKE_object_where_is_calc_mat4(Object *ob, float r_obmat[4][4]) -{ - if (ob->parent) { - Object *par = ob->parent; - solve_parenting(ob, par, false, r_obmat, NULL); - } - else { - BKE_object_to_mat4(ob, r_obmat); - } -} - -void BKE_object_where_is_calc_ex( - Depsgraph *depsgraph, Scene *scene, RigidBodyWorld *rbw, Object *ob, float r_originmat[3][3]) -{ - float ctime = DEG_get_ctime(depsgraph); - object_where_is_calc_ex(depsgraph, scene, ob, ctime, rbw, r_originmat); -} -void BKE_object_where_is_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) -{ - float ctime = DEG_get_ctime(depsgraph); - object_where_is_calc_ex(depsgraph, scene, ob, ctime, NULL, NULL); -} - -/** - * For calculation of the inverse parent transform, only used for editor. - * - * It assumes the object parent is already in the depsgraph. - * Otherwise, after changing ob->parent you need to call: - * - #DEG_relations_tag_update(bmain); - * - #BKE_scene_graph_update_tagged(depsgraph, bmain); - */ -void BKE_object_workob_calc_parent(Depsgraph *depsgraph, Scene *scene, Object *ob, Object *workob) -{ - BKE_object_workob_clear(workob); - - unit_m4(workob->obmat); - unit_m4(workob->parentinv); - unit_m4(workob->constinv); - - /* Since this is used while calculating parenting, - * at this moment ob_eval->parent is still NULL. */ - workob->parent = DEG_get_evaluated_object(depsgraph, ob->parent); - - workob->trackflag = ob->trackflag; - workob->upflag = ob->upflag; - - workob->partype = ob->partype; - workob->par1 = ob->par1; - workob->par2 = ob->par2; - workob->par3 = ob->par3; - - /* The effects of constraints should NOT be included in the parent-inverse matrix. Constraints - * are supposed to be applied after the object's local loc/rot/scale. If the (inverted) effect of - * constraints would be included in the parent inverse matrix, these would be applied before the - * object's local loc/rot/scale instead of after. For example, a "Copy Rotation" constraint would - * rotate the object's local translation as well. See T82156. */ - - BLI_strncpy(workob->parsubstr, ob->parsubstr, sizeof(workob->parsubstr)); - - BKE_object_where_is_calc(depsgraph, scene, workob); -} - -/** - * Applies the global transformation \a mat to the \a ob using a relative parent space if - * supplied. - * - * \param mat: the global transformation mat that the object should be set object to. - * \param parent: the parent space in which this object will be set relative to - * (should probably always be parent_eval). - * \param use_compat: true to ensure that rotations are set using the - * min difference between the old and new orientation. - */ -void BKE_object_apply_mat4_ex(Object *ob, - const float mat[4][4], - Object *parent, - const float parentinv[4][4], - const bool use_compat) -{ - /* see BKE_pchan_apply_mat4() for the equivalent 'pchan' function */ - - float rot[3][3]; - - if (parent != NULL) { - float rmat[4][4], diff_mat[4][4], imat[4][4], parent_mat[4][4]; - - BKE_object_get_parent_matrix(ob, parent, parent_mat); - - mul_m4_m4m4(diff_mat, parent_mat, parentinv); - invert_m4_m4(imat, diff_mat); - mul_m4_m4m4(rmat, imat, mat); /* get the parent relative matrix */ - - /* same as below, use rmat rather than mat */ - mat4_to_loc_rot_size(ob->loc, rot, ob->scale, rmat); - } - else { - mat4_to_loc_rot_size(ob->loc, rot, ob->scale, mat); - } - - BKE_object_mat3_to_rot(ob, rot, use_compat); - - sub_v3_v3(ob->loc, ob->dloc); - - if (ob->dscale[0] != 0.0f) { - ob->scale[0] /= ob->dscale[0]; - } - if (ob->dscale[1] != 0.0f) { - ob->scale[1] /= ob->dscale[1]; - } - if (ob->dscale[2] != 0.0f) { - ob->scale[2] /= ob->dscale[2]; - } - - /* BKE_object_mat3_to_rot handles delta rotations */ -} - -/** - * XXX: should be removed after COW operators port to use BKE_object_apply_mat4_ex directly. - */ -void BKE_object_apply_mat4(Object *ob, - const float mat[4][4], - const bool use_compat, - const bool use_parent) -{ - BKE_object_apply_mat4_ex(ob, mat, use_parent ? ob->parent : NULL, ob->parentinv, use_compat); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Object Bounding Box API - * \{ */ - -BoundBox *BKE_boundbox_alloc_unit(void) -{ - const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; - - BoundBox *bb = MEM_callocN(sizeof(BoundBox), "OB-BoundBox"); - BKE_boundbox_init_from_minmax(bb, min, max); - - return bb; -} - -void BKE_boundbox_init_from_minmax(BoundBox *bb, const float min[3], const float max[3]) -{ - bb->vec[0][0] = bb->vec[1][0] = bb->vec[2][0] = bb->vec[3][0] = min[0]; - bb->vec[4][0] = bb->vec[5][0] = bb->vec[6][0] = bb->vec[7][0] = max[0]; - - bb->vec[0][1] = bb->vec[1][1] = bb->vec[4][1] = bb->vec[5][1] = min[1]; - bb->vec[2][1] = bb->vec[3][1] = bb->vec[6][1] = bb->vec[7][1] = max[1]; - - bb->vec[0][2] = bb->vec[3][2] = bb->vec[4][2] = bb->vec[7][2] = min[2]; - bb->vec[1][2] = bb->vec[2][2] = bb->vec[5][2] = bb->vec[6][2] = max[2]; -} - -void BKE_boundbox_calc_center_aabb(const BoundBox *bb, float r_cent[3]) -{ - r_cent[0] = 0.5f * (bb->vec[0][0] + bb->vec[4][0]); - r_cent[1] = 0.5f * (bb->vec[0][1] + bb->vec[2][1]); - r_cent[2] = 0.5f * (bb->vec[0][2] + bb->vec[1][2]); -} - -void BKE_boundbox_calc_size_aabb(const BoundBox *bb, float r_size[3]) -{ - r_size[0] = 0.5f * fabsf(bb->vec[0][0] - bb->vec[4][0]); - r_size[1] = 0.5f * fabsf(bb->vec[0][1] - bb->vec[2][1]); - r_size[2] = 0.5f * fabsf(bb->vec[0][2] - bb->vec[1][2]); -} - -void BKE_boundbox_minmax(const BoundBox *bb, - const float obmat[4][4], - float r_min[3], - float r_max[3]) -{ - int i; - for (i = 0; i < 8; i++) { - float vec[3]; - mul_v3_m4v3(vec, obmat, bb->vec[i]); - minmax_v3v3_v3(r_min, r_max, vec); - } -} - -BoundBox *BKE_object_boundbox_get(Object *ob) -{ - BoundBox *bb = NULL; - - switch (ob->type) { - case OB_MESH: - bb = BKE_mesh_boundbox_get(ob); - break; - case OB_CURVE: - case OB_SURF: - case OB_FONT: - bb = BKE_curve_boundbox_get(ob); - break; - case OB_MBALL: - bb = BKE_mball_boundbox_get(ob); - break; - case OB_LATTICE: - bb = BKE_lattice_boundbox_get(ob); - break; - case OB_ARMATURE: - bb = BKE_armature_boundbox_get(ob); - break; - case OB_GPENCIL: - bb = BKE_gpencil_boundbox_get(ob); - break; - case OB_HAIR: - bb = BKE_hair_boundbox_get(ob); - break; - case OB_POINTCLOUD: - bb = BKE_pointcloud_boundbox_get(ob); - break; - case OB_VOLUME: - bb = BKE_volume_boundbox_get(ob); - break; - default: - break; - } - return bb; -} - -/** - * Use this to temporally disable/enable bound-box. - */ -void BKE_object_boundbox_flag(Object *ob, int flag, const bool set) -{ - BoundBox *bb = BKE_object_boundbox_get(ob); - if (bb) { - if (set) { - bb->flag |= flag; - } - else { - bb->flag &= ~flag; - } - } -} - -void BKE_object_boundbox_calc_from_mesh(struct Object *ob, const struct Mesh *me_eval) -{ - float min[3], max[3]; - - INIT_MINMAX(min, max); - - if (!BKE_mesh_wrapper_minmax(me_eval, min, max)) { - zero_v3(min); - zero_v3(max); - } - - if (ob->runtime.bb == NULL) { - ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "DM-BoundBox"); - } - - BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); - - ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Object Dimension Get/Set - * - * \warning Setting dimensions is prone to feedback loops in evaluation. - * \{ */ - -void BKE_object_dimensions_get(Object *ob, float r_vec[3]) -{ - BoundBox *bb = BKE_object_boundbox_get(ob); - if (bb) { - float scale[3]; - - mat4_to_size(scale, ob->obmat); - - r_vec[0] = fabsf(scale[0]) * (bb->vec[4][0] - bb->vec[0][0]); - r_vec[1] = fabsf(scale[1]) * (bb->vec[2][1] - bb->vec[0][1]); - r_vec[2] = fabsf(scale[2]) * (bb->vec[1][2] - bb->vec[0][2]); - } - else { - zero_v3(r_vec); - } -} - -/** - * The original scale and object matrix can be passed in so any difference - * of the objects matrix and the final matrix can be accounted for, - * typically this caused by parenting, constraints or delta-scale. - * - * Re-using these values from the object causes a feedback loop - * when multiple values are modified at once in some situations. see: T69536. - */ -void BKE_object_dimensions_set_ex(Object *ob, - const float value[3], - int axis_mask, - const float ob_scale_orig[3], - const float ob_obmat_orig[4][4]) -{ - BoundBox *bb = BKE_object_boundbox_get(ob); - if (bb) { - float len[3]; - - len[0] = bb->vec[4][0] - bb->vec[0][0]; - len[1] = bb->vec[2][1] - bb->vec[0][1]; - len[2] = bb->vec[1][2] - bb->vec[0][2]; - - for (int i = 0; i < 3; i++) { - if (((1 << i) & axis_mask) == 0) { - - if (ob_scale_orig != NULL) { - const float scale_delta = len_v3(ob_obmat_orig[i]) / ob_scale_orig[i]; - if (isfinite(scale_delta)) { - len[i] *= scale_delta; - } - } - - const float scale = copysignf(value[i] / len[i], ob->scale[i]); - if (isfinite(scale)) { - ob->scale[i] = scale; - } - } - } - } -} - -void BKE_object_dimensions_set(Object *ob, const float value[3], int axis_mask) -{ - BKE_object_dimensions_set_ex(ob, value, axis_mask, NULL, NULL); -} - -void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool use_hidden) -{ - float vec[3]; - bool changed = false; - - switch (ob->type) { - case OB_CURVE: - case OB_FONT: - case OB_SURF: { - BoundBox bb = *BKE_curve_boundbox_get(ob); - BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); - changed = true; - break; - } - case OB_MESH: { - BoundBox bb = *BKE_mesh_boundbox_get(ob); - BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); - changed = true; - break; - } - case OB_GPENCIL: { - BoundBox bb = *BKE_gpencil_boundbox_get(ob); - BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); - changed = true; - break; - } - case OB_LATTICE: { - Lattice *lt = ob->data; - BPoint *bp = lt->def; - int u, v, w; - - for (w = 0; w < lt->pntsw; w++) { - for (v = 0; v < lt->pntsv; v++) { - for (u = 0; u < lt->pntsu; u++, bp++) { - mul_v3_m4v3(vec, ob->obmat, bp->vec); - minmax_v3v3_v3(r_min, r_max, vec); - } - } - } - changed = true; - break; - } - case OB_ARMATURE: { - changed = BKE_pose_minmax(ob, r_min, r_max, use_hidden, false); - break; - } - case OB_MBALL: { - float ob_min[3], ob_max[3]; - - changed = BKE_mball_minmax_ex(ob->data, ob_min, ob_max, ob->obmat, 0); - if (changed) { - minmax_v3v3_v3(r_min, r_max, ob_min); - minmax_v3v3_v3(r_min, r_max, ob_max); - } - break; - } - case OB_HAIR: { - BoundBox bb = *BKE_hair_boundbox_get(ob); - BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); - changed = true; - break; - } - - case OB_POINTCLOUD: { - BoundBox bb = *BKE_pointcloud_boundbox_get(ob); - BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); - changed = true; - break; - } - case OB_VOLUME: { - BoundBox bb = *BKE_volume_boundbox_get(ob); - BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); - changed = true; - break; - } - } - - if (changed == false) { - float size[3]; - - copy_v3_v3(size, ob->scale); - if (ob->type == OB_EMPTY) { - mul_v3_fl(size, ob->empty_drawsize); - } - - minmax_v3v3_v3(r_min, r_max, ob->obmat[3]); - - copy_v3_v3(vec, ob->obmat[3]); - add_v3_v3(vec, size); - minmax_v3v3_v3(r_min, r_max, vec); - - copy_v3_v3(vec, ob->obmat[3]); - sub_v3_v3(vec, size); - minmax_v3v3_v3(r_min, r_max, vec); - } -} - -void BKE_object_empty_draw_type_set(Object *ob, const int value) -{ - ob->empty_drawtype = value; - - if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { - if (!ob->iuser) { - ob->iuser = MEM_callocN(sizeof(ImageUser), "image user"); - ob->iuser->flag |= IMA_ANIM_ALWAYS; - ob->iuser->frames = 100; - ob->iuser->sfra = 1; - } - } - else { - MEM_SAFE_FREE(ob->iuser); - } -} - -bool BKE_object_empty_image_frame_is_visible_in_view3d(const Object *ob, const RegionView3D *rv3d) -{ - const char visibility_flag = ob->empty_image_visibility_flag; - if (rv3d->is_persp) { - return (visibility_flag & OB_EMPTY_IMAGE_HIDE_PERSPECTIVE) == 0; - } - - return (visibility_flag & OB_EMPTY_IMAGE_HIDE_ORTHOGRAPHIC) == 0; -} - -bool BKE_object_empty_image_data_is_visible_in_view3d(const Object *ob, const RegionView3D *rv3d) -{ - /* Caller is expected to check this. */ - BLI_assert(BKE_object_empty_image_frame_is_visible_in_view3d(ob, rv3d)); - - const char visibility_flag = ob->empty_image_visibility_flag; - - if ((visibility_flag & (OB_EMPTY_IMAGE_HIDE_BACK | OB_EMPTY_IMAGE_HIDE_FRONT)) != 0) { - float eps, dot; - if (rv3d->is_persp) { - /* NOTE: we could normalize the 'view_dir' then use 'eps' - * however the issue with empty objects being visible when viewed from the side - * is only noticeable in orthographic views. */ - float view_dir[3]; - sub_v3_v3v3(view_dir, rv3d->viewinv[3], ob->obmat[3]); - dot = dot_v3v3(ob->obmat[2], view_dir); - eps = 0.0f; - } - else { - dot = dot_v3v3(ob->obmat[2], rv3d->viewinv[2]); - eps = 1e-5f; - } - if (visibility_flag & OB_EMPTY_IMAGE_HIDE_BACK) { - if (dot < eps) { - return false; - } - } - if (visibility_flag & OB_EMPTY_IMAGE_HIDE_FRONT) { - if (dot > -eps) { - return false; - } - } - } - - if (visibility_flag & OB_EMPTY_IMAGE_HIDE_NON_AXIS_ALIGNED) { - float proj[3]; - project_plane_v3_v3v3(proj, ob->obmat[2], rv3d->viewinv[2]); - const float proj_length_sq = len_squared_v3(proj); - if (proj_length_sq > 1e-5f) { - return false; - } - } - - return true; -} - -bool BKE_object_minmax_dupli(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - float r_min[3], - float r_max[3], - const bool use_hidden) -{ - bool ok = false; - if ((ob->transflag & OB_DUPLI) == 0 && ob->runtime.geometry_set_eval == NULL) { - return ok; - } - - DupliObject *dob; - ListBase *lb = object_duplilist(depsgraph, scene, ob); - for (dob = lb->first; dob; dob = dob->next) { - if ((use_hidden == false) && (dob->no_draw != 0)) { - /* pass */ - } - else { - BoundBox *bb = BKE_object_boundbox_get(dob->ob); - - if (bb) { - int i; - for (i = 0; i < 8; i++) { - float vec[3]; - mul_v3_m4v3(vec, dob->mat, bb->vec[i]); - minmax_v3v3_v3(r_min, r_max, vec); - } - - ok = true; - } - } - } - free_object_duplilist(lb); /* does restore */ - - return ok; -} - -struct GPencilStrokePointIterData { - const float (*obmat)[4]; - - void (*point_func_cb)(const float co[3], void *user_data); - void *user_data; -}; - -static void foreach_display_point_gpencil_stroke_fn(bGPDlayer *UNUSED(layer), - bGPDframe *UNUSED(frame), - bGPDstroke *stroke, - void *thunk) -{ - struct GPencilStrokePointIterData *iter_data = thunk; - { - bGPDspoint *pt; - int i; - for (i = 0, pt = stroke->points; i < stroke->totpoints; i++, pt++) { - float co[3]; - mul_v3_m4v3(co, iter_data->obmat, &pt->x); - iter_data->point_func_cb(co, iter_data->user_data); - } - } -} - -void BKE_object_foreach_display_point(Object *ob, - const float obmat[4][4], - void (*func_cb)(const float[3], void *), - void *user_data) -{ - /* TODO: pointcloud and hair objects support */ - const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - float co[3]; - - if (mesh_eval != NULL) { - const MVert *mv = mesh_eval->mvert; - const int totvert = mesh_eval->totvert; - for (int i = 0; i < totvert; i++, mv++) { - mul_v3_m4v3(co, obmat, mv->co); - func_cb(co, user_data); - } - } - else if (ob->type == OB_GPENCIL) { - struct GPencilStrokePointIterData iter_data = { - .obmat = obmat, .point_func_cb = func_cb, .user_data = user_data}; - - BKE_gpencil_visible_stroke_iter( - ob->data, NULL, foreach_display_point_gpencil_stroke_fn, &iter_data); - } - else if (ob->runtime.curve_cache && ob->runtime.curve_cache->disp.first) { - DispList *dl; - - for (dl = ob->runtime.curve_cache->disp.first; dl; dl = dl->next) { - const float *v3 = dl->verts; - int totvert = dl->nr; - int i; - - for (i = 0; i < totvert; i++, v3 += 3) { - mul_v3_m4v3(co, obmat, v3); - func_cb(co, user_data); - } - } - } -} - -void BKE_scene_foreach_display_point(Depsgraph *depsgraph, - void (*func_cb)(const float[3], void *), - void *user_data) -{ - DEG_OBJECT_ITER_BEGIN (depsgraph, - ob, - DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE | - DEG_ITER_OBJECT_FLAG_DUPLI) { - if ((ob->base_flag & BASE_SELECTED) != 0) { - BKE_object_foreach_display_point(ob, ob->obmat, func_cb, user_data); - } - } - DEG_OBJECT_ITER_END; -} - -/** - * Struct members from DNA_object_types.h - */ -typedef struct ObTfmBack { - float loc[3], dloc[3]; - /** scale and delta scale. */ - float scale[3], dscale[3]; - /** euler rotation. */ - float rot[3], drot[3]; - /** quaternion rotation. */ - float quat[4], dquat[4]; - /** axis angle rotation - axis part. */ - float rotAxis[3], drotAxis[3]; - /** axis angle rotation - angle part. */ - float rotAngle, drotAngle; - /** final worldspace matrix with constraints & animsys applied. */ - float obmat[4][4]; - /** inverse result of parent, so that object doesn't 'stick' to parent. */ - float parentinv[4][4]; - /** inverse result of constraints. doesn't include effect of parent or object local transform. - */ - float constinv[4][4]; - /** inverse matrix of 'obmat' for during render, temporally: ipokeys of transform. */ - float imat[4][4]; -} ObTfmBack; - -void *BKE_object_tfm_backup(Object *ob) -{ - ObTfmBack *obtfm = MEM_mallocN(sizeof(ObTfmBack), "ObTfmBack"); - copy_v3_v3(obtfm->loc, ob->loc); - copy_v3_v3(obtfm->dloc, ob->dloc); - copy_v3_v3(obtfm->scale, ob->scale); - copy_v3_v3(obtfm->dscale, ob->dscale); - copy_v3_v3(obtfm->rot, ob->rot); - copy_v3_v3(obtfm->drot, ob->drot); - copy_qt_qt(obtfm->quat, ob->quat); - copy_qt_qt(obtfm->dquat, ob->dquat); - copy_v3_v3(obtfm->rotAxis, ob->rotAxis); - copy_v3_v3(obtfm->drotAxis, ob->drotAxis); - obtfm->rotAngle = ob->rotAngle; - obtfm->drotAngle = ob->drotAngle; - copy_m4_m4(obtfm->obmat, ob->obmat); - copy_m4_m4(obtfm->parentinv, ob->parentinv); - copy_m4_m4(obtfm->constinv, ob->constinv); - copy_m4_m4(obtfm->imat, ob->imat); - - return (void *)obtfm; -} - -void BKE_object_tfm_restore(Object *ob, void *obtfm_pt) -{ - ObTfmBack *obtfm = (ObTfmBack *)obtfm_pt; - copy_v3_v3(ob->loc, obtfm->loc); - copy_v3_v3(ob->dloc, obtfm->dloc); - copy_v3_v3(ob->scale, obtfm->scale); - copy_v3_v3(ob->dscale, obtfm->dscale); - copy_v3_v3(ob->rot, obtfm->rot); - copy_v3_v3(ob->drot, obtfm->drot); - copy_qt_qt(ob->quat, obtfm->quat); - copy_qt_qt(ob->dquat, obtfm->dquat); - copy_v3_v3(ob->rotAxis, obtfm->rotAxis); - copy_v3_v3(ob->drotAxis, obtfm->drotAxis); - ob->rotAngle = obtfm->rotAngle; - ob->drotAngle = obtfm->drotAngle; - copy_m4_m4(ob->obmat, obtfm->obmat); - copy_m4_m4(ob->parentinv, obtfm->parentinv); - copy_m4_m4(ob->constinv, obtfm->constinv); - copy_m4_m4(ob->imat, obtfm->imat); -} - -bool BKE_object_parent_loop_check(const Object *par, const Object *ob) -{ - /* test if 'ob' is a parent somewhere in par's parents */ - if (par == NULL) { - return false; - } - if (ob == par) { - return true; - } - return BKE_object_parent_loop_check(par->parent, ob); -} - -static void object_handle_update_proxy(Depsgraph *depsgraph, - Scene *scene, - Object *object, - const bool do_proxy_update) -{ - /* The case when this is a collection proxy, object_update is called in collection.c */ - if (object->proxy == NULL) { - return; - } - /* set pointer in library proxy target, for copying, but restore it */ - object->proxy->proxy_from = object; - // printf("set proxy pointer for later collection stuff %s\n", ob->id.name); - - /* the no-group proxy case, we call update */ - if (object->proxy_group == NULL) { - if (do_proxy_update) { - // printf("call update, lib ob %s proxy %s\n", ob->proxy->id.name, ob->id.name); - BKE_object_handle_update(depsgraph, scene, object->proxy); - } - } -} - -/** - * Proxy rule: - * - lib_object->proxy_from == the one we borrow from, only set temporal and cleared here. - * - local_object->proxy == pointer to library object, saved in files and read. - * - * Function below is polluted with proxy exceptions, cleanup will follow! - * - * The main object update call, for object matrix, constraints, keys and displist (modifiers) - * requires flags to be set! - * - * Ideally we shouldn't have to pass the rigid body world, - * but need bigger restructuring to avoid id. - */ -void BKE_object_handle_update_ex(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - RigidBodyWorld *rbw, - const bool do_proxy_update) -{ - const ID *object_data = ob->data; - const bool recalc_object = (ob->id.recalc & ID_RECALC_ALL) != 0; - const bool recalc_data = (object_data != NULL) ? ((object_data->recalc & ID_RECALC_ALL) != 0) : - 0; - if (!recalc_object && !recalc_data) { - object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update); - return; - } - /* Speed optimization for animation lookups. */ - if (ob->pose != NULL) { - BKE_pose_channels_hash_ensure(ob->pose); - if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { - BKE_pose_update_constraint_flags(ob->pose); - } - } - if (recalc_data) { - if (ob->type == OB_ARMATURE) { - /* this happens for reading old files and to match library armatures - * with poses we do it ahead of BKE_object_where_is_calc to ensure animation - * is evaluated on the rebuilt pose, otherwise we get incorrect poses - * on file load */ - if (ob->pose == NULL || (ob->pose->flag & POSE_RECALC)) { - /* No need to pass bmain here, we assume we do not need to rebuild DEG from here... */ - BKE_pose_rebuild(NULL, ob, ob->data, true); - } - } - } - /* XXX new animsys warning: depsgraph tag ID_RECALC_GEOMETRY should not skip drivers, - * which is only in BKE_object_where_is_calc now */ - /* XXX: should this case be ID_RECALC_TRANSFORM instead? */ - if (recalc_object || recalc_data) { - if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) { - printf("recalcob %s\n", ob->id.name + 2); - } - /* Handle proxy copy for target. */ - if (!BKE_object_eval_proxy_copy(depsgraph, ob)) { - BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, NULL); - } - } - - if (recalc_data) { - BKE_object_handle_data_update(depsgraph, scene, ob); - } - - object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update); -} - -/** - * \warning "scene" here may not be the scene object actually resides in. - * When dealing with background-sets, "scene" is actually the active scene. - * e.g. "scene" <-- set 1 <-- set 2 ("ob" lives here) <-- set 3 <-- ... <-- set n - * rigid bodies depend on their world so use #BKE_object_handle_update_ex() - * to also pass along the current rigid body world. - */ -void BKE_object_handle_update(Depsgraph *depsgraph, Scene *scene, Object *ob) -{ - BKE_object_handle_update_ex(depsgraph, scene, ob, NULL, true); -} - -void BKE_object_sculpt_data_create(Object *ob) -{ - BLI_assert((ob->sculpt == NULL) && (ob->mode & OB_MODE_ALL_SCULPT)); - ob->sculpt = MEM_callocN(sizeof(SculptSession), __func__); - ob->sculpt->mode_type = ob->mode; -} - -bool BKE_object_obdata_texspace_get(Object *ob, short **r_texflag, float **r_loc, float **r_size) -{ - - if (ob->data == NULL) { - return false; - } - - switch (GS(((ID *)ob->data)->name)) { - case ID_ME: { - BKE_mesh_texspace_get_reference((Mesh *)ob->data, r_texflag, r_loc, r_size); - break; - } - case ID_CU: { - Curve *cu = ob->data; - BKE_curve_texspace_ensure(cu); - if (r_texflag) { - *r_texflag = &cu->texflag; - } - if (r_loc) { - *r_loc = cu->loc; - } - if (r_size) { - *r_size = cu->size; - } - break; - } - case ID_MB: { - MetaBall *mb = ob->data; - if (r_texflag) { - *r_texflag = &mb->texflag; - } - if (r_loc) { - *r_loc = mb->loc; - } - if (r_size) { - *r_size = mb->size; - } - break; - } - default: - return false; - } - return true; -} - -/** Get evaluated mesh for given object. */ -Mesh *BKE_object_get_evaluated_mesh(const Object *object) -{ - ID *data_eval = object->runtime.data_eval; - return (data_eval && GS(data_eval->name) == ID_ME) ? (Mesh *)data_eval : NULL; -} - -/** - * Get mesh which is not affected by modifiers: - * - For original objects it will be same as `object->data`, and it is a mesh - * which is in the corresponding #Main. - * - For copied-on-write objects it will give pointer to a copied-on-write - * mesh which corresponds to original object's mesh. - */ -Mesh *BKE_object_get_pre_modified_mesh(const Object *object) -{ - if (object->type == OB_MESH && object->runtime.data_orig != NULL) { - BLI_assert(object->id.tag & LIB_TAG_COPIED_ON_WRITE); - BLI_assert(object->id.orig_id != NULL); - BLI_assert(object->runtime.data_orig->orig_id == ((Object *)object->id.orig_id)->data); - Mesh *result = (Mesh *)object->runtime.data_orig; - BLI_assert((result->id.tag & LIB_TAG_COPIED_ON_WRITE) != 0); - BLI_assert((result->id.tag & LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT) == 0); - return result; - } - BLI_assert((object->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0); - return object->data; -} - -/** - * Get a mesh which corresponds to the very original mesh from #Main. - * - For original objects it will be object->data. - * - For evaluated objects it will be same mesh as corresponding original - * object uses as data. - */ -Mesh *BKE_object_get_original_mesh(const Object *object) -{ - Mesh *result = NULL; - if (object->id.orig_id == NULL) { - BLI_assert((object->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0); - result = object->data; - } - else { - BLI_assert((object->id.tag & LIB_TAG_COPIED_ON_WRITE) != 0); - result = ((Object *)object->id.orig_id)->data; - } - BLI_assert(result != NULL); - BLI_assert((result->id.tag & (LIB_TAG_COPIED_ON_WRITE | LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT)) == - 0); - return result; -} - -Lattice *BKE_object_get_lattice(const Object *object) -{ - ID *data = object->data; - if (data == NULL || GS(data->name) != ID_LT) { - return NULL; - } - - Lattice *lt = (Lattice *)data; - if (lt->editlatt) { - return lt->editlatt->latt; - } - - return lt; -} - -Lattice *BKE_object_get_evaluated_lattice(const Object *object) -{ - ID *data_eval = object->runtime.data_eval; - - if (data_eval == NULL || GS(data_eval->name) != ID_LT) { - return NULL; - } - - Lattice *lt_eval = (Lattice *)data_eval; - if (lt_eval->editlatt) { - return lt_eval->editlatt->latt; - } - - return lt_eval; -} - -static int pc_cmp(const void *a, const void *b) -{ - const LinkData *ad = a, *bd = b; - if (POINTER_AS_INT(ad->data) > POINTER_AS_INT(bd->data)) { - return 1; - } - - return 0; -} - -/* TODO: Review the usages of this function, currently with COW it will be called for orig object - * and then again for COW copies of it, think this is bad since there is no guarantee that we get - * the same stack index in both cases? Order is important since this index is used for filenames on - * disk. */ -int BKE_object_insert_ptcache(Object *ob) -{ - LinkData *link = NULL; - int i = 0; - - BLI_listbase_sort(&ob->pc_ids, pc_cmp); - - for (link = ob->pc_ids.first, i = 0; link; link = link->next, i++) { - int index = POINTER_AS_INT(link->data); - - if (i < index) { - break; - } - } - - link = MEM_callocN(sizeof(LinkData), "PCLink"); - link->data = POINTER_FROM_INT(i); - BLI_addtail(&ob->pc_ids, link); - - return i; -} - -static int pc_findindex(ListBase *listbase, int index) -{ - int number = 0; - - if (listbase == NULL) { - return -1; - } - - LinkData *link = listbase->first; - while (link) { - if (POINTER_AS_INT(link->data) == index) { - return number; - } - - number++; - link = link->next; - } - - return -1; -} - -void BKE_object_delete_ptcache(Object *ob, int index) -{ - int list_index = pc_findindex(&ob->pc_ids, index); - LinkData *link = BLI_findlink(&ob->pc_ids, list_index); - BLI_freelinkN(&ob->pc_ids, link); -} - -/* -------------------------------------------------------------------- */ -/** \name Object Data Shape Key Insert - * \{ */ - -/** Mesh */ -static KeyBlock *insert_meshkey(Main *bmain, Object *ob, const char *name, const bool from_mix) -{ - Mesh *me = ob->data; - Key *key = me->key; - KeyBlock *kb; - int newkey = 0; - - if (key == NULL) { - key = me->key = BKE_key_add(bmain, (ID *)me); - key->type = KEY_RELATIVE; - newkey = 1; - } - - if (newkey || from_mix == false) { - /* create from mesh */ - kb = BKE_keyblock_add_ctime(key, name, false); - BKE_keyblock_convert_from_mesh(me, key, kb); - } - else { - /* copy from current values */ - int totelem; - float *data = BKE_key_evaluate_object(ob, &totelem); - - /* create new block with prepared data */ - kb = BKE_keyblock_add_ctime(key, name, false); - kb->data = data; - kb->totelem = totelem; - } - - return kb; -} -/** Lattice */ -static KeyBlock *insert_lattkey(Main *bmain, Object *ob, const char *name, const bool from_mix) -{ - Lattice *lt = ob->data; - Key *key = lt->key; - KeyBlock *kb; - int newkey = 0; - - if (key == NULL) { - key = lt->key = BKE_key_add(bmain, (ID *)lt); - key->type = KEY_RELATIVE; - newkey = 1; - } - - if (newkey || from_mix == false) { - kb = BKE_keyblock_add_ctime(key, name, false); - if (!newkey) { - KeyBlock *basekb = (KeyBlock *)key->block.first; - kb->data = MEM_dupallocN(basekb->data); - kb->totelem = basekb->totelem; - } - else { - BKE_keyblock_convert_from_lattice(lt, kb); - } - } - else { - /* copy from current values */ - int totelem; - float *data = BKE_key_evaluate_object(ob, &totelem); - - /* create new block with prepared data */ - kb = BKE_keyblock_add_ctime(key, name, false); - kb->totelem = totelem; - kb->data = data; - } - - return kb; -} -/** Curve */ -static KeyBlock *insert_curvekey(Main *bmain, Object *ob, const char *name, const bool from_mix) -{ - Curve *cu = ob->data; - Key *key = cu->key; - KeyBlock *kb; - ListBase *lb = BKE_curve_nurbs_get(cu); - int newkey = 0; - - if (key == NULL) { - key = cu->key = BKE_key_add(bmain, (ID *)cu); - key->type = KEY_RELATIVE; - newkey = 1; - } - - if (newkey || from_mix == false) { - /* create from curve */ - kb = BKE_keyblock_add_ctime(key, name, false); - if (!newkey) { - KeyBlock *basekb = (KeyBlock *)key->block.first; - kb->data = MEM_dupallocN(basekb->data); - kb->totelem = basekb->totelem; - } - else { - BKE_keyblock_convert_from_curve(cu, kb, lb); - } - } - else { - /* copy from current values */ - int totelem; - float *data = BKE_key_evaluate_object(ob, &totelem); - - /* create new block with prepared data */ - kb = BKE_keyblock_add_ctime(key, name, false); - kb->totelem = totelem; - kb->data = data; - } - - return kb; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Object Shape Key API - * \{ */ - -KeyBlock *BKE_object_shapekey_insert(Main *bmain, - Object *ob, - const char *name, - const bool from_mix) -{ - KeyBlock *key = NULL; - - switch (ob->type) { - case OB_MESH: - key = insert_meshkey(bmain, ob, name, from_mix); - break; - case OB_CURVE: - case OB_SURF: - key = insert_curvekey(bmain, ob, name, from_mix); - break; - case OB_LATTICE: - key = insert_lattkey(bmain, ob, name, from_mix); - break; - default: - break; - } - - /* Set the first active when none is set when called from RNA. */ - if (key != NULL) { - if (ob->shapenr <= 0) { - ob->shapenr = 1; - } - } - - return key; -} - -bool BKE_object_shapekey_free(Main *bmain, Object *ob) -{ - Key **key_p, *key; - - key_p = BKE_key_from_object_p(ob); - if (ELEM(NULL, key_p, *key_p)) { - return false; - } - - key = *key_p; - *key_p = NULL; - - BKE_id_free_us(bmain, key); - - return true; -} - -bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb) -{ - KeyBlock *rkb; - Key *key = BKE_key_from_object(ob); - short kb_index; - - if (key == NULL) { - return false; - } - - kb_index = BLI_findindex(&key->block, kb); - BLI_assert(kb_index != -1); - - for (rkb = key->block.first; rkb; rkb = rkb->next) { - if (rkb->relative == kb_index) { - /* remap to the 'Basis' */ - rkb->relative = 0; - } - else if (rkb->relative >= kb_index) { - /* Fix positional shift of the keys when kb is deleted from the list */ - rkb->relative -= 1; - } - } - - BLI_remlink(&key->block, kb); - key->totkey--; - if (key->refkey == kb) { - key->refkey = key->block.first; - - if (key->refkey) { - /* apply new basis key on original data */ - switch (ob->type) { - case OB_MESH: - BKE_keyblock_convert_to_mesh(key->refkey, ob->data); - break; - case OB_CURVE: - case OB_SURF: - BKE_keyblock_convert_to_curve(key->refkey, ob->data, BKE_curve_nurbs_get(ob->data)); - break; - case OB_LATTICE: - BKE_keyblock_convert_to_lattice(key->refkey, ob->data); - break; - } - } - } - - if (kb->data) { - MEM_freeN(kb->data); - } - MEM_freeN(kb); - - /* Unset active when all are freed. */ - if (BLI_listbase_is_empty(&key->block)) { - ob->shapenr = 0; - } - else if (ob->shapenr > 1) { - ob->shapenr--; - } - - if (key->totkey == 0) { - BKE_object_shapekey_free(bmain, ob); - } - - return true; -} - -/** \} */ - -bool BKE_object_flag_test_recursive(const Object *ob, short flag) -{ - if (ob->flag & flag) { - return true; - } - if (ob->parent) { - return BKE_object_flag_test_recursive(ob->parent, flag); - } - - return false; -} - -bool BKE_object_is_child_recursive(const Object *ob_parent, const Object *ob_child) -{ - for (ob_child = ob_child->parent; ob_child; ob_child = ob_child->parent) { - if (ob_child == ob_parent) { - return true; - } - } - return false; -} - -/** - * Most important if this is modified it should _always_ return true, in certain - * cases false positives are hard to avoid (shape keys for example). - */ -int BKE_object_is_modified(Scene *scene, Object *ob) -{ - /* Always test on original object since evaluated object may no longer - * have shape keys or modifiers that were used to evaluate it. */ - ob = DEG_get_original_object(ob); - - int flag = 0; - - if (BKE_key_from_object(ob)) { - flag |= eModifierMode_Render | eModifierMode_Realtime; - } - else { - ModifierData *md; - VirtualModifierData virtualModifierData; - /* cloth */ - for (md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); - md && (flag != (eModifierMode_Render | eModifierMode_Realtime)); - md = md->next) { - if ((flag & eModifierMode_Render) == 0 && - BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) { - flag |= eModifierMode_Render; - } - - if ((flag & eModifierMode_Realtime) == 0 && - BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { - flag |= eModifierMode_Realtime; - } - } - } - - return flag; -} - -/** - * Check of objects moves in time. - * - * \note This function is currently optimized for usage in combination - * with modifier deformation checks (#eModifierTypeType_OnlyDeform), - * so modifiers can quickly check if their target objects moves - * (causing deformation motion blur) or not. - * - * This makes it possible to give some degree of false-positives here, - * but it's currently an acceptable tradeoff between complexity and check - * speed. In combination with checks of modifier stack and real life usage - * percentage of false-positives shouldn't be that high. - * - * \note This function does not consider physics systems. - */ -bool BKE_object_moves_in_time(const Object *object, bool recurse_parent) -{ - /* If object has any sort of animation data assume it is moving. */ - if (BKE_animdata_id_is_animated(&object->id)) { - return true; - } - if (!BLI_listbase_is_empty(&object->constraints)) { - return true; - } - if (recurse_parent && object->parent != NULL) { - return BKE_object_moves_in_time(object->parent, true); - } - return false; -} - -static bool object_moves_in_time(const Object *object) -{ - return BKE_object_moves_in_time(object, true); -} - -static bool object_deforms_in_time(Object *object) -{ - if (BKE_key_from_object(object) != NULL) { - return true; - } - if (!BLI_listbase_is_empty(&object->modifiers)) { - return true; - } - return object_moves_in_time(object); -} - -static bool constructive_modifier_is_deform_modified(Object *ob, ModifierData *md) -{ - /* TODO(sergey): Consider generalizing this a bit so all modifier logic - * is concentrated in MOD_{modifier}.c file, - */ - if (md->type == eModifierType_Array) { - ArrayModifierData *amd = (ArrayModifierData *)md; - /* TODO(sergey): Check if curve is deformed. */ - return (amd->start_cap != NULL && object_moves_in_time(amd->start_cap)) || - (amd->end_cap != NULL && object_moves_in_time(amd->end_cap)) || - (amd->curve_ob != NULL && object_moves_in_time(amd->curve_ob)) || - (amd->offset_ob != NULL && object_moves_in_time(amd->offset_ob)); - } - if (md->type == eModifierType_Mirror) { - MirrorModifierData *mmd = (MirrorModifierData *)md; - return mmd->mirror_ob != NULL && - (object_moves_in_time(mmd->mirror_ob) || object_moves_in_time(ob)); - } - if (md->type == eModifierType_Screw) { - ScrewModifierData *smd = (ScrewModifierData *)md; - return smd->ob_axis != NULL && object_moves_in_time(smd->ob_axis); - } - if (md->type == eModifierType_MeshSequenceCache) { - /* NOTE: Not ideal because it's unknown whether topology changes or not. - * This will be detected later, so by assuming it's only deformation - * going on here we allow baking deform-only mesh to Alembic and have - * proper motion blur after that. - */ - return true; - } - if (md->type == eModifierType_Nodes) { - /* Not ideal for performance to always assume this is animated, - * but hard to detect in general. The better long term solution is likely - * to replace BKE_object_is_deform_modified by a test if the object was - * modified by the depsgraph when changing frames. */ - return true; - } - return false; -} - -static bool modifiers_has_animation_check(const Object *ob) -{ - /* TODO(sergey): This is a bit code duplication with depsgraph, but - * would be nicer to solve this as a part of new dependency graph - * work, so we avoid conflicts and so. - */ - if (ob->adt != NULL) { - AnimData *adt = ob->adt; - FCurve *fcu; - if (adt->action != NULL) { - for (fcu = adt->action->curves.first; fcu; fcu = fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, "modifiers[")) { - return true; - } - } - } - for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, "modifiers[")) { - return true; - } - } - } - return false; -} - -/** - * Test if object is affected by deforming modifiers (for motion blur). again - * most important is to avoid false positives, this is to skip computations - * and we can still if there was actual deformation afterwards. - */ -int BKE_object_is_deform_modified(Scene *scene, Object *ob) -{ - /* Always test on original object since evaluated object may no longer - * have shape keys or modifiers that were used to evaluate it. */ - ob = DEG_get_original_object(ob); - - ModifierData *md; - VirtualModifierData virtualModifierData; - int flag = 0; - const bool is_modifier_animated = modifiers_has_animation_check(ob); - - if (BKE_key_from_object(ob)) { - flag |= eModifierMode_Realtime | eModifierMode_Render; - } - - if (ob->type == OB_CURVE) { - Curve *cu = (Curve *)ob->data; - if (cu->taperobj != NULL && object_deforms_in_time(cu->taperobj)) { - flag |= eModifierMode_Realtime | eModifierMode_Render; - } - } - - /* cloth */ - for (md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); - md && (flag != (eModifierMode_Render | eModifierMode_Realtime)); - md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); - bool can_deform = mti->type == eModifierTypeType_OnlyDeform || is_modifier_animated; - - if (!can_deform) { - can_deform = constructive_modifier_is_deform_modified(ob, md); - } - - if (can_deform) { - if (!(flag & eModifierMode_Render) && - BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) { - flag |= eModifierMode_Render; - } - - if (!(flag & eModifierMode_Realtime) && - BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { - flag |= eModifierMode_Realtime; - } - } - } - - return flag; -} - -/** Return the number of scenes using (instantiating) that object in their collections. */ -int BKE_object_scenes_users_get(Main *bmain, Object *ob) -{ - int num_scenes = 0; - for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { - if (BKE_collection_has_object_recursive(scene->master_collection, ob)) { - num_scenes++; - } - } - return num_scenes; -} - -MovieClip *BKE_object_movieclip_get(Scene *scene, Object *ob, bool use_default) -{ - MovieClip *clip = use_default ? scene->clip : NULL; - bConstraint *con = ob->constraints.first, *scon = NULL; - - while (con) { - if (con->type == CONSTRAINT_TYPE_CAMERASOLVER) { - if (scon == NULL || (scon->flag & CONSTRAINT_OFF)) { - scon = con; - } - } - - con = con->next; - } - - if (scon) { - bCameraSolverConstraint *solver = scon->data; - if ((solver->flag & CAMERASOLVER_ACTIVECLIP) == 0) { - clip = solver->clip; - } - else { - clip = scene->clip; - } - } - - return clip; -} - -void BKE_object_runtime_reset(Object *object) -{ - memset(&object->runtime, 0, sizeof(object->runtime)); -} - -/** - * Reset all pointers which we don't want to be shared when copying the object. - */ -void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag)) -{ - Object_Runtime *runtime = &object->runtime; - runtime->data_eval = NULL; - runtime->gpd_eval = NULL; - runtime->mesh_deform_eval = NULL; - runtime->curve_cache = NULL; - runtime->object_as_temp_mesh = NULL; - runtime->object_as_temp_curve = NULL; - runtime->geometry_set_eval = NULL; -} - -/** - * The function frees memory used by the runtime data, but not the runtime field itself. - * - * All runtime data is cleared to ensure it's not used again, - * in keeping with other `_free_data(..)` functions. - */ -void BKE_object_runtime_free_data(Object *object) -{ - /* Currently this is all that's needed. */ - BKE_object_free_derived_caches(object); - - BKE_object_runtime_reset(object); -} - -/** - * Find an associated armature object. - */ -static Object *obrel_armature_find(Object *ob) -{ - Object *ob_arm = NULL; - - if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) { - ob_arm = ob->parent; - } - else { - ModifierData *mod; - for (mod = (ModifierData *)ob->modifiers.first; mod; mod = mod->next) { - if (mod->type == eModifierType_Armature) { - ob_arm = ((ArmatureModifierData *)mod)->object; - } - } - } - - return ob_arm; -} - -static bool obrel_list_test(Object *ob) -{ - return ob && !(ob->id.tag & LIB_TAG_DOIT); -} - -static void obrel_list_add(LinkNode **links, Object *ob) -{ - BLI_linklist_prepend(links, ob); - ob->id.tag |= LIB_TAG_DOIT; -} - -/** - * Iterates over all objects of the given scene layer. - * Depending on the #eObjectSet flag: - * collect either #OB_SET_ALL, #OB_SET_VISIBLE or #OB_SET_SELECTED objects. - * If #OB_SET_VISIBLE or#OB_SET_SELECTED are collected, - * then also add related objects according to the given \a includeFilter. - */ -LinkNode *BKE_object_relational_superset(struct ViewLayer *view_layer, - eObjectSet objectSet, - eObRelationTypes includeFilter) -{ - LinkNode *links = NULL; - - Base *base; - - /* Remove markers from all objects */ - for (base = view_layer->object_bases.first; base; base = base->next) { - base->object->id.tag &= ~LIB_TAG_DOIT; - } - - /* iterate over all selected and visible objects */ - for (base = view_layer->object_bases.first; base; base = base->next) { - if (objectSet == OB_SET_ALL) { - /* as we get all anyways just add it */ - Object *ob = base->object; - obrel_list_add(&links, ob); - } - else { - if ((objectSet == OB_SET_SELECTED && BASE_SELECTED_EDITABLE(((View3D *)NULL), base)) || - (objectSet == OB_SET_VISIBLE && BASE_EDITABLE(((View3D *)NULL), base))) { - Object *ob = base->object; - - if (obrel_list_test(ob)) { - obrel_list_add(&links, ob); - } - - /* parent relationship */ - if (includeFilter & (OB_REL_PARENT | OB_REL_PARENT_RECURSIVE)) { - Object *parent = ob->parent; - if (obrel_list_test(parent)) { - - obrel_list_add(&links, parent); - - /* recursive parent relationship */ - if (includeFilter & OB_REL_PARENT_RECURSIVE) { - parent = parent->parent; - while (obrel_list_test(parent)) { - - obrel_list_add(&links, parent); - parent = parent->parent; - } - } - } - } - - /* child relationship */ - if (includeFilter & (OB_REL_CHILDREN | OB_REL_CHILDREN_RECURSIVE)) { - Base *local_base; - for (local_base = view_layer->object_bases.first; local_base; - local_base = local_base->next) { - if (BASE_EDITABLE(((View3D *)NULL), local_base)) { - - Object *child = local_base->object; - if (obrel_list_test(child)) { - if ((includeFilter & OB_REL_CHILDREN_RECURSIVE && - BKE_object_is_child_recursive(ob, child)) || - (includeFilter & OB_REL_CHILDREN && child->parent && child->parent == ob)) { - obrel_list_add(&links, child); - } - } - } - } - } - - /* include related armatures */ - if (includeFilter & OB_REL_MOD_ARMATURE) { - Object *arm = obrel_armature_find(ob); - if (obrel_list_test(arm)) { - obrel_list_add(&links, arm); - } - } - } - } - } - - return links; -} - -/** - * return all groups this object is a part of, caller must free. - */ -struct LinkNode *BKE_object_groups(Main *bmain, Scene *scene, Object *ob) -{ - LinkNode *collection_linknode = NULL; - Collection *collection = NULL; - while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) { - BLI_linklist_prepend(&collection_linknode, collection); - } - - return collection_linknode; -} - -void BKE_object_groups_clear(Main *bmain, Scene *scene, Object *ob) -{ - Collection *collection = NULL; - while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) { - BKE_collection_object_remove(bmain, collection, ob, false); - DEG_id_tag_update(&collection->id, ID_RECALC_COPY_ON_WRITE); - } -} - -/** - * Return a KDTree_3d from the deformed object (in worldspace) - * - * \note Only mesh objects currently support deforming, others are TODO. - * - * \param ob: - * \param r_tot: - * \return The kdtree or NULL if it can't be created. - */ -KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot) -{ - KDTree_3d *tree = NULL; - unsigned int tot = 0; - - switch (ob->type) { - case OB_MESH: { - Mesh *me = ob->data; - unsigned int i; - - Mesh *me_eval = ob->runtime.mesh_deform_eval ? ob->runtime.mesh_deform_eval : - BKE_object_get_evaluated_mesh(ob); - const int *index; - - if (me_eval && (index = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX))) { - MVert *mvert = me_eval->mvert; - uint totvert = me_eval->totvert; - - /* tree over-allocs in case where some verts have ORIGINDEX_NONE */ - tot = 0; - tree = BLI_kdtree_3d_new(totvert); - - /* We don't how many verts from the DM we can use. */ - for (i = 0; i < totvert; i++) { - if (index[i] != ORIGINDEX_NONE) { - float co[3]; - mul_v3_m4v3(co, ob->obmat, mvert[i].co); - BLI_kdtree_3d_insert(tree, index[i], co); - tot++; - } - } - } - else { - MVert *mvert = me->mvert; - - tot = me->totvert; - tree = BLI_kdtree_3d_new(tot); - - for (i = 0; i < tot; i++) { - float co[3]; - mul_v3_m4v3(co, ob->obmat, mvert[i].co); - BLI_kdtree_3d_insert(tree, i, co); - } - } - - BLI_kdtree_3d_balance(tree); - break; - } - case OB_CURVE: - case OB_SURF: { - /* TODO: take deformation into account */ - Curve *cu = ob->data; - unsigned int i, a; - - Nurb *nu; - - tot = BKE_nurbList_verts_count_without_handles(&cu->nurb); - tree = BLI_kdtree_3d_new(tot); - i = 0; - - nu = cu->nurb.first; - while (nu) { - if (nu->bezt) { - BezTriple *bezt; - - bezt = nu->bezt; - a = nu->pntsu; - while (a--) { - float co[3]; - mul_v3_m4v3(co, ob->obmat, bezt->vec[1]); - BLI_kdtree_3d_insert(tree, i++, co); - bezt++; - } - } - else { - BPoint *bp; - - bp = nu->bp; - a = nu->pntsu * nu->pntsv; - while (a--) { - float co[3]; - mul_v3_m4v3(co, ob->obmat, bp->vec); - BLI_kdtree_3d_insert(tree, i++, co); - bp++; - } - } - nu = nu->next; - } - - BLI_kdtree_3d_balance(tree); - break; - } - case OB_LATTICE: { - /* TODO: take deformation into account */ - Lattice *lt = ob->data; - BPoint *bp; - unsigned int i; - - tot = lt->pntsu * lt->pntsv * lt->pntsw; - tree = BLI_kdtree_3d_new(tot); - i = 0; - - for (bp = lt->def; i < tot; bp++) { - float co[3]; - mul_v3_m4v3(co, ob->obmat, bp->vec); - BLI_kdtree_3d_insert(tree, i++, co); - } - - BLI_kdtree_3d_balance(tree); - break; - } - } - - *r_tot = tot; - return tree; -} - -bool BKE_object_modifier_use_time(Scene *scene, - Object *ob, - ModifierData *md, - const int dag_eval_mode) -{ - if (BKE_modifier_depends_ontime(scene, md, dag_eval_mode)) { - return true; - } - - /* Check whether modifier is animated. */ - /* TODO: this should be handled as part of build_animdata() -- Aligorith */ - if (ob->adt) { - AnimData *adt = ob->adt; - FCurve *fcu; - - char md_name_esc[sizeof(md->name) * 2]; - BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc)); - - char pattern[sizeof(md_name_esc) + 16]; - BLI_snprintf(pattern, sizeof(pattern), "modifiers[\"%s\"]", md_name_esc); - - /* action - check for F-Curves with paths containing 'modifiers[' */ - if (adt->action) { - for (fcu = (FCurve *)adt->action->curves.first; fcu != NULL; fcu = (FCurve *)fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - /* This here allows modifier properties to get driven and still update properly - * - * Workaround to get T26764 (e.g. subsurf levels not updating when animated/driven) - * working, without the updating problems (T28525 T28690 T28774 T28777) caused - * by the RNA updates cache introduced in r.38649 - */ - for (fcu = (FCurve *)adt->drivers.first; fcu != NULL; fcu = (FCurve *)fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - - /* XXX: also, should check NLA strips, though for now assume that nobody uses - * that and we can omit that for performance reasons... */ - } - - return false; -} - -bool BKE_object_modifier_gpencil_use_time(Object *ob, GpencilModifierData *md) -{ - if (BKE_gpencil_modifier_depends_ontime(md)) { - return true; - } - - /* Check whether modifier is animated. */ - /* TODO(Aligorith): this should be handled as part of build_animdata() */ - if (ob->adt) { - AnimData *adt = ob->adt; - FCurve *fcu; - - char md_name_esc[sizeof(md->name) * 2]; - BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc)); - - char pattern[sizeof(md_name_esc) + 32]; - BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md_name_esc); - - /* action - check for F-Curves with paths containing 'grease_pencil_modifiers[' */ - if (adt->action) { - for (fcu = adt->action->curves.first; fcu != NULL; fcu = fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - /* This here allows modifier properties to get driven and still update properly */ - for (fcu = adt->drivers.first; fcu != NULL; fcu = fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - return false; -} - -bool BKE_object_shaderfx_use_time(Object *ob, ShaderFxData *fx) -{ - if (BKE_shaderfx_depends_ontime(fx)) { - return true; - } - - /* Check whether effect is animated. */ - /* TODO(Aligorith): this should be handled as part of build_animdata() */ - if (ob->adt) { - AnimData *adt = ob->adt; - FCurve *fcu; - - char fx_name_esc[sizeof(fx->name) * 2]; - BLI_str_escape(fx_name_esc, fx->name, sizeof(fx_name_esc)); - - char pattern[sizeof(fx_name_esc) + 32]; - BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx_name_esc); - - /* action - check for F-Curves with paths containing string[' */ - if (adt->action) { - for (fcu = adt->action->curves.first; fcu != NULL; fcu = fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - /* This here allows properties to get driven and still update properly */ - for (fcu = adt->drivers.first; fcu != NULL; fcu = fcu->next) { - if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { - return true; - } - } - } - - return false; -} - -/** - * Set "ignore cache" flag for all caches on this object. - */ -static void object_cacheIgnoreClear(Object *ob, int state) -{ - ListBase pidlist; - PTCacheID *pid; - BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0); - - for (pid = pidlist.first; pid; pid = pid->next) { - if (pid->cache) { - if (state) { - pid->cache->flag |= PTCACHE_IGNORE_CLEAR; - } - else { - pid->cache->flag &= ~PTCACHE_IGNORE_CLEAR; - } - } - } - - BLI_freelistN(&pidlist); -} - -/** - * \note this function should eventually be replaced by depsgraph functionality. - * Avoid calling this in new code unless there is a very good reason for it! - */ -bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - bool update_mesh, - int parent_recursion, - float frame, - int type) -{ - const bool flush_to_original = DEG_is_active(depsgraph); - ModifierData *md = BKE_modifiers_findby_type(ob, (ModifierType)type); - bConstraint *con; - - if (type == eModifierType_DynamicPaint) { - DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; - - /* if other is dynamic paint canvas, don't update */ - if (pmd && pmd->canvas) { - return true; - } - } - else if (type == eModifierType_Fluid) { - FluidModifierData *fmd = (FluidModifierData *)md; - - if (fmd && (fmd->type & MOD_FLUID_TYPE_DOMAIN) != 0) { - return true; - } - } - - /* if object has parents, update them too */ - if (parent_recursion) { - int recursion = parent_recursion - 1; - bool no_update = false; - if (ob->parent) { - no_update |= BKE_object_modifier_update_subframe( - depsgraph, scene, ob->parent, 0, recursion, frame, type); - } - if (ob->track) { - no_update |= BKE_object_modifier_update_subframe( - depsgraph, scene, ob->track, 0, recursion, frame, type); - } - - /* skip subframe if object is parented - * to vertex of a dynamic paint canvas */ - if (no_update && (ELEM(ob->partype, PARVERT1, PARVERT3))) { - return false; - } - - /* also update constraint targets */ - for (con = ob->constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - - if (cti && cti->get_constraint_targets) { - bConstraintTarget *ct; - cti->get_constraint_targets(con, &targets); - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar) { - BKE_object_modifier_update_subframe( - depsgraph, scene, ct->tar, 0, recursion, frame, type); - } - } - /* free temp targets */ - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - } - - /* was originally ID_RECALC_ALL - TODO: which flags are really needed??? */ - /* TODO(sergey): What about animation? */ - const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, - frame); - - ob->id.recalc |= ID_RECALC_ALL; - if (update_mesh) { - BKE_animsys_evaluate_animdata( - &ob->id, ob->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); - /* ignore cache clear during subframe updates - * to not mess up cache validity */ - object_cacheIgnoreClear(ob, 1); - BKE_object_handle_update(depsgraph, scene, ob); - object_cacheIgnoreClear(ob, 0); - } - else { - BKE_object_where_is_calc_time(depsgraph, scene, ob, frame); - } - - /* for curve following objects, parented curve has to be updated too */ - if (ob->type == OB_CURVE) { - Curve *cu = ob->data; - BKE_animsys_evaluate_animdata( - &cu->id, cu->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); - } - /* and armatures... */ - if (ob->type == OB_ARMATURE) { - bArmature *arm = ob->data; - BKE_animsys_evaluate_animdata( - &arm->id, arm->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); - BKE_pose_where_is(depsgraph, scene, ob); - } - - return false; -} - -/** - * Updates select_id of all objects in the given \a bmain. - */ -void BKE_object_update_select_id(struct Main *bmain) -{ - Object *ob = bmain->objects.first; - int select_id = 1; - while (ob) { - ob->runtime.select_id = select_id++; - ob = ob->id.next; - } -} - -Mesh *BKE_object_to_mesh(Depsgraph *depsgraph, Object *object, bool preserve_all_data_layers) -{ - BKE_object_to_mesh_clear(object); - - Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false); - object->runtime.object_as_temp_mesh = mesh; - return mesh; -} - -void BKE_object_to_mesh_clear(Object *object) -{ - if (object->runtime.object_as_temp_mesh == NULL) { - return; - } - BKE_id_free(NULL, object->runtime.object_as_temp_mesh); - object->runtime.object_as_temp_mesh = NULL; -} - -Curve *BKE_object_to_curve(Object *object, Depsgraph *depsgraph, bool apply_modifiers) -{ - BKE_object_to_curve_clear(object); - - Curve *curve = BKE_curve_new_from_object(object, depsgraph, apply_modifiers); - object->runtime.object_as_temp_curve = curve; - return curve; -} - -void BKE_object_to_curve_clear(Object *object) -{ - if (object->runtime.object_as_temp_curve == NULL) { - return; - } - BKE_id_free(NULL, object->runtime.object_as_temp_curve); - object->runtime.object_as_temp_curve = NULL; -} - -void BKE_object_check_uuids_unique_and_report(const Object *object) -{ - BKE_pose_check_uuids_unique_and_report(object->pose); - BKE_modifier_check_uuids_unique_and_report(object); -} - -void BKE_object_modifiers_lib_link_common(void *userData, - struct Object *ob, - struct ID **idpoin, - int cb_flag) -{ - BlendLibReader *reader = userData; - - BLO_read_id_address(reader, ob->id.lib, idpoin); - if (*idpoin != NULL && (cb_flag & IDWALK_CB_USER) != 0) { - id_us_plus_no_lib(*idpoin); - } -} - -void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data) -{ - ob->type = BKE_object_obdata_to_type(new_data); - ob->data = new_data; - ob->runtime.geometry_set_eval = NULL; - ob->runtime.data_eval = NULL; - if (ob->runtime.bb != NULL) { - ob->runtime.bb->flag |= BOUNDBOX_DIRTY; - } - ob->id.py_instance = NULL; -} - -bool BKE_object_supports_material_slots(struct Object *ob) -{ - return ELEM(ob->type, - OB_MESH, - OB_CURVE, - OB_SURF, - OB_FONT, - OB_MBALL, - OB_HAIR, - OB_POINTCLOUD, - OB_VOLUME, - OB_GPENCIL); -} diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc new file mode 100644 index 00000000000..495b1f0dafd --- /dev/null +++ b/source/blender/blenkernel/intern/object.cc @@ -0,0 +1,5851 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +/* Allow using deprecated functionality for .blend file I/O. */ +#define DNA_DEPRECATED_ALLOW + +#include +#include +#include + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_collection_types.h" +#include "DNA_constraint_types.h" +#include "DNA_defaults.h" +#include "DNA_dynamicpaint_types.h" +#include "DNA_effect_types.h" +#include "DNA_fluid_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_key_types.h" +#include "DNA_lattice_types.h" +#include "DNA_light_types.h" +#include "DNA_lightprobe_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_meta_types.h" +#include "DNA_movieclip_types.h" +#include "DNA_nla_types.h" +#include "DNA_object_fluidsim_types.h" +#include "DNA_object_types.h" +#include "DNA_pointcloud_types.h" +#include "DNA_rigidbody_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_sequence_types.h" +#include "DNA_shader_fx_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" +#include "DNA_world_types.h" + +#include "BLI_blenlib.h" +#include "BLI_kdtree.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_action.h" +#include "BKE_anim_data.h" +#include "BKE_anim_path.h" +#include "BKE_anim_visualization.h" +#include "BKE_animsys.h" +#include "BKE_armature.h" +#include "BKE_asset.h" +#include "BKE_camera.h" +#include "BKE_collection.h" +#include "BKE_constraint.h" +#include "BKE_curve.h" +#include "BKE_deform.h" +#include "BKE_displist.h" +#include "BKE_duplilist.h" +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_effect.h" +#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" +#include "BKE_geometry_set.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_hair.h" +#include "BKE_icons.h" +#include "BKE_idprop.h" +#include "BKE_idtype.h" +#include "BKE_image.h" +#include "BKE_key.h" +#include "BKE_lattice.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_lib_remap.h" +#include "BKE_light.h" +#include "BKE_lightprobe.h" +#include "BKE_linestyle.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mball.h" +#include "BKE_mesh.h" +#include "BKE_mesh_wrapper.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_object_facemap.h" +#include "BKE_paint.h" +#include "BKE_particle.h" +#include "BKE_pbvh.h" +#include "BKE_pointcache.h" +#include "BKE_pointcloud.h" +#include "BKE_rigidbody.h" +#include "BKE_scene.h" +#include "BKE_shader_fx.h" +#include "BKE_softbody.h" +#include "BKE_speaker.h" +#include "BKE_subdiv_ccg.h" +#include "BKE_subsurf.h" +#include "BKE_vfont.h" +#include "BKE_volume.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "DRW_engine.h" + +#include "BLO_read_write.h" +#include "BLO_readfile.h" + +#include "SEQ_sequencer.h" + +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + +#include "CCGSubSurf.h" +#include "atomic_ops.h" + +static CLG_LogRef LOG = {"bke.object"}; + +/** + * Vertex parent modifies original BMesh which is not safe for threading. + * Ideally such a modification should be handled as a separate DAG update + * callback for mesh datablock, but for until it is actually supported use + * simpler solution with a mutex lock. + * - sergey - + */ +#define VPARENT_THREADING_HACK + +#ifdef VPARENT_THREADING_HACK +static ThreadMutex vparent_lock = BLI_MUTEX_INITIALIZER; +#endif + +static void copy_object_pose(Object *obn, const Object *ob, const int flag); + +static void object_init_data(ID *id) +{ + Object *ob = (Object *)id; + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(ob, id)); + + MEMCPY_STRUCT_AFTER(ob, DNA_struct_default_get(Object), id); + + ob->type = OB_EMPTY; + + ob->trackflag = OB_POSY; + ob->upflag = OB_POSZ; + + /* Animation Visualization defaults */ + animviz_settings_init(&ob->avs); +} + +static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int flag) +{ + Object *ob_dst = (Object *)id_dst; + const Object *ob_src = (const Object *)id_src; + + /* Do not copy runtime data. */ + BKE_object_runtime_reset_on_copy(ob_dst, flag); + + /* We never handle usercount here for own data. */ + const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; + + if (ob_src->totcol) { + ob_dst->mat = (Material **)MEM_dupallocN(ob_src->mat); + ob_dst->matbits = (char *)MEM_dupallocN(ob_src->matbits); + ob_dst->totcol = ob_src->totcol; + } + else if (ob_dst->mat != nullptr || ob_dst->matbits != nullptr) { + /* This shall not be needed, but better be safe than sorry. */ + BLI_assert_msg( + 0, "Object copy: non-nullptr material pointers with zero counter, should not happen."); + ob_dst->mat = nullptr; + ob_dst->matbits = nullptr; + } + + if (ob_src->iuser) { + ob_dst->iuser = (ImageUser *)MEM_dupallocN(ob_src->iuser); + } + + if (ob_src->runtime.bb) { + ob_dst->runtime.bb = (BoundBox *)MEM_dupallocN(ob_src->runtime.bb); + } + + BLI_listbase_clear(&ob_dst->shader_fx); + LISTBASE_FOREACH (ShaderFxData *, fx, &ob_src->shader_fx) { + ShaderFxData *nfx = BKE_shaderfx_new(fx->type); + BLI_strncpy(nfx->name, fx->name, sizeof(nfx->name)); + BKE_shaderfx_copydata_ex(fx, nfx, flag_subdata); + BLI_addtail(&ob_dst->shader_fx, nfx); + } + + if (ob_src->pose) { + copy_object_pose(ob_dst, ob_src, flag_subdata); + /* backwards compat... non-armatures can get poses in older files? */ + if (ob_src->type == OB_ARMATURE) { + const bool do_pose_id_user = (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0; + BKE_pose_rebuild(bmain, ob_dst, (bArmature *)ob_dst->data, do_pose_id_user); + } + } + + BKE_object_facemap_copy_list(&ob_dst->fmaps, &ob_src->fmaps); + BKE_constraints_copy_ex(&ob_dst->constraints, &ob_src->constraints, flag_subdata, true); + + ob_dst->mode = ob_dst->type != OB_GPENCIL ? OB_MODE_OBJECT : ob_dst->mode; + ob_dst->sculpt = nullptr; + + if (ob_src->pd) { + ob_dst->pd = (PartDeflect *)MEM_dupallocN(ob_src->pd); + if (ob_dst->pd->rng) { + ob_dst->pd->rng = (RNG *)MEM_dupallocN(ob_src->pd->rng); + } + } + BKE_rigidbody_object_copy(bmain, ob_dst, ob_src, flag_subdata); + + BLI_listbase_clear(&ob_dst->modifiers); + BLI_listbase_clear(&ob_dst->greasepencil_modifiers); + /* NOTE: Also takes care of softbody and particle systems copying. */ + BKE_object_modifier_stack_copy(ob_dst, ob_src, true, flag_subdata); + + BLI_listbase_clear((ListBase *)&ob_dst->drawdata); + BLI_listbase_clear(&ob_dst->pc_ids); + + ob_dst->avs = ob_src->avs; + ob_dst->mpath = animviz_copy_motionpath(ob_src->mpath); + + /* Do not copy object's preview + * (mostly due to the fact renderers create temp copy of objects). */ + if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0 && false) { /* XXX TODO: temp hack. */ + BKE_previewimg_id_copy(&ob_dst->id, &ob_src->id); + } + else { + ob_dst->preview = nullptr; + } +} + +static void object_free_data(ID *id) +{ + Object *ob = (Object *)id; + + DRW_drawdata_free((ID *)ob); + + /* BKE__free shall never touch to ID->us. Never ever. */ + BKE_object_free_modifiers(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); + BKE_object_free_shaderfx(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); + + MEM_SAFE_FREE(ob->mat); + MEM_SAFE_FREE(ob->matbits); + MEM_SAFE_FREE(ob->iuser); + MEM_SAFE_FREE(ob->runtime.bb); + + BLI_freelistN(&ob->fmaps); + if (ob->pose) { + BKE_pose_free_ex(ob->pose, false); + ob->pose = nullptr; + } + if (ob->mpath) { + animviz_free_motionpath(ob->mpath); + ob->mpath = nullptr; + } + + BKE_constraints_free_ex(&ob->constraints, false); + + BKE_partdeflect_free(ob->pd); + BKE_rigidbody_free_object(ob, nullptr); + BKE_rigidbody_free_constraint(ob); + + sbFree(ob); + + BKE_sculptsession_free(ob); + + BLI_freelistN(&ob->pc_ids); + + /* Free runtime curves data. */ + if (ob->runtime.curve_cache) { + BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); + if (ob->runtime.curve_cache->anim_path_accum_length) { + MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length); + } + MEM_freeN(ob->runtime.curve_cache); + ob->runtime.curve_cache = nullptr; + } + + BKE_previewimg_free(&ob->preview); +} + +static void object_make_local(Main *bmain, ID *id, const int flags) +{ + if (!ID_IS_LINKED(id)) { + return; + } + + Object *ob = (Object *)id; + const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; + const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0; + bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + BLI_assert(force_copy == false || force_copy != force_local); + + bool is_local = false, is_lib = false; + + /* - only lib users: do nothing (unless force_local is set) + * - only local users: set flag + * - mixed: make copy + * In case we make a whole lib's content local, + * we always want to localize, and we skip remapping (done later). + */ + + if (!force_local && !force_copy) { + BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); + if (lib_local || is_local) { + if (!is_lib) { + force_local = true; + } + else { + force_copy = true; + } + } + } + + if (force_local) { + BKE_lib_id_clear_library_data(bmain, &ob->id, flags); + BKE_lib_id_expand_local(bmain, &ob->id, flags); + if (clear_proxy) { + if (ob->proxy_from != nullptr) { + ob->proxy_from->proxy = nullptr; + ob->proxy_from->proxy_group = nullptr; + } + ob->proxy = ob->proxy_from = ob->proxy_group = nullptr; + } + } + else if (force_copy) { + Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id); + id_us_min(&ob_new->id); + + ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = nullptr; + + /* setting newid is mandatory for complex make_lib_local logic... */ + ID_NEW_SET(ob, ob_new); + + if (!lib_local) { + BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE); + } + } +} + +static void library_foreach_modifiersForeachIDLink(void *user_data, + Object *UNUSED(object), + ID **id_pointer, + int cb_flag) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); +} + +static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, + Object *UNUSED(object), + ID **id_pointer, + int cb_flag) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); +} + +static void library_foreach_shaderfxForeachIDLink(void *user_data, + Object *UNUSED(object), + ID **id_pointer, + int cb_flag) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); +} + +static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), + ID **id_pointer, + bool is_reference, + void *user_data) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + const int cb_flag = is_reference ? IDWALK_CB_USER : IDWALK_CB_NOP; + BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); +} + +static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(psys), + ID **id_pointer, + void *user_data, + int cb_flag) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); +} + +static void object_foreach_id(ID *id, LibraryForeachIDData *data) +{ + Object *object = (Object *)id; + + /* Object is special, proxies make things hard... */ + const int proxy_cb_flag = ((BKE_lib_query_foreachid_process_flags_get(data) & + IDWALK_NO_INDIRECT_PROXY_DATA_USAGE) == 0 && + (object->proxy || object->proxy_group)) ? + IDWALK_CB_INDIRECT_USAGE : + 0; + + /* object data special case */ + if (object->type == OB_EMPTY) { + /* empty can have nullptr or Image */ + BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, proxy_cb_flag | IDWALK_CB_USER); + } + else { + /* when set, this can't be nullptr */ + if (object->data) { + BKE_LIB_FOREACHID_PROCESS_ID( + data, object->data, proxy_cb_flag | IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); + } + } + + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF); + /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP); + + /* Special case! + * Since this field is set/owned by 'user' of this ID (and not ID itself), + * it is only indirect usage if proxy object is linked... Twisted. */ + { + const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( + data, + (object->proxy_from != nullptr && ID_IS_LINKED(object->proxy_from)) ? + IDWALK_CB_INDIRECT_USAGE : + 0, + true); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); + BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); + } + + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER); + + for (int i = 0; i < object->totcol; i++) { + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); + } + + /* Note that ob->gpd is deprecated, so no need to handle it here. */ + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->instance_collection, IDWALK_CB_USER); + + if (object->pd) { + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->f_source, IDWALK_CB_NOP); + } + /* Note that ob->effect is deprecated, so no need to handle it here. */ + + if (object->pose) { + const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( + data, proxy_cb_flag, false); + LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + IDP_foreach_property( + pchan->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pchan->custom, IDWALK_CB_USER); + BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, data); + } + BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); + } + + if (object->rigidbody_constraint) { + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); + } + + BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data); + BKE_gpencil_modifiers_foreach_ID_link( + object, library_foreach_gpencil_modifiersForeachIDLink, data); + BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data); + BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data); + + LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { + BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data); + } + + if (object->soft) { + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->soft->collision_group, IDWALK_CB_NOP); + + if (object->soft->effector_weights) { + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->soft->effector_weights->group, IDWALK_CB_NOP); + } + } +} + +static void write_fmaps(BlendWriter *writer, ListBase *fbase) +{ + LISTBASE_FOREACH (bFaceMap *, fmap, fbase) { + BLO_write_struct(writer, bFaceMap, fmap); + } +} + +static void object_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + Object *ob = (Object *)id; + + const bool is_undo = BLO_write_is_undo(writer); + + /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ + BKE_object_runtime_reset(ob); + + if (is_undo) { + /* For undo we stay in object mode during undo presses, so keep edit-mode disabled on save as + * well, can help reducing false detection of changed data-blocks. */ + ob->mode &= ~OB_MODE_EDIT; + } + + /* write LibData */ + BLO_write_id_struct(writer, Object, id_address, &ob->id); + BKE_id_blend_write(writer, &ob->id); + + if (ob->adt) { + BKE_animdata_blend_write(writer, ob->adt); + } + + /* direct data */ + BLO_write_pointer_array(writer, ob->totcol, ob->mat); + BLO_write_raw(writer, sizeof(char) * ob->totcol, ob->matbits); + + bArmature *arm = nullptr; + if (ob->type == OB_ARMATURE) { + arm = (bArmature *)ob->data; + if (arm && ob->pose && arm->act_bone) { + BLI_strncpy(ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone)); + } + } + + BKE_pose_blend_write(writer, ob->pose, arm); + write_fmaps(writer, &ob->fmaps); + BKE_constraint_blend_write(writer, &ob->constraints); + animviz_motionpath_blend_write(writer, ob->mpath); + + BLO_write_struct(writer, PartDeflect, ob->pd); + if (ob->soft) { + /* Set deprecated pointers to prevent crashes of older Blenders */ + ob->soft->pointcache = ob->soft->shared->pointcache; + ob->soft->ptcaches = ob->soft->shared->ptcaches; + BLO_write_struct(writer, SoftBody, ob->soft); + BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared); + BKE_ptcache_blend_write(writer, &(ob->soft->shared->ptcaches)); + BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); + } + + if (ob->rigidbody_object) { + /* TODO: if any extra data is added to handle duplis, will need separate function then */ + BLO_write_struct(writer, RigidBodyOb, ob->rigidbody_object); + } + if (ob->rigidbody_constraint) { + BLO_write_struct(writer, RigidBodyCon, ob->rigidbody_constraint); + } + + if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { + BLO_write_struct(writer, ImageUser, ob->iuser); + } + + BKE_particle_system_blend_write(writer, &ob->particlesystem); + BKE_modifier_blend_write(writer, &ob->modifiers); + BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers); + BKE_shaderfx_blend_write(writer, &ob->shader_fx); + + BLO_write_struct_list(writer, LinkData, &ob->pc_ids); + + BKE_previewimg_blend_write(writer, ob->preview); +} + +/* XXX deprecated - old animation system */ +static void direct_link_nlastrips(BlendDataReader *reader, ListBase *strips) +{ + BLO_read_list(reader, strips); + + LISTBASE_FOREACH (bActionStrip *, strip, strips) { + BLO_read_list(reader, &strip->modifiers); + } +} + +static void object_blend_read_data(BlendDataReader *reader, ID *id) +{ + Object *ob = (Object *)id; + + PartEff *paf; + + /* XXX This should not be needed - but seems like it can happen in some cases, + * so for now play safe. */ + ob->proxy_from = nullptr; + + const bool is_undo = BLO_read_data_is_undo(reader); + if (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT)) { + /* Do not allow any non-object mode for linked data. + * See T34776, T42780, T81027 for more information. */ + ob->mode &= ~OB_MODE_ALL_MODE_DATA; + } + else if (is_undo) { + /* For undo we want to stay in object mode during undo presses, so keep some edit modes + * disabled. + * TODO: Check if we should not disable more edit modes here? */ + ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT); + } + + BLO_read_data_address(reader, &ob->adt); + BKE_animdata_blend_read_data(reader, ob->adt); + + BLO_read_data_address(reader, &ob->pose); + BKE_pose_blend_read_data(reader, ob->pose); + + BLO_read_data_address(reader, &ob->mpath); + if (ob->mpath) { + animviz_motionpath_blend_read_data(reader, ob->mpath); + } + + /* Only for versioning, vertex group names are now stored on object data. */ + BLO_read_list(reader, &ob->defbase); + + BLO_read_list(reader, &ob->fmaps); + /* XXX deprecated - old animation system <<< */ + direct_link_nlastrips(reader, &ob->nlastrips); + BLO_read_list(reader, &ob->constraintChannels); + /* >>> XXX deprecated - old animation system */ + + BLO_read_pointer_array(reader, (void **)&ob->mat); + BLO_read_data_address(reader, &ob->matbits); + + /* do it here, below old data gets converted */ + BKE_modifier_blend_read_data(reader, &ob->modifiers, ob); + BKE_gpencil_modifier_blend_read_data(reader, &ob->greasepencil_modifiers); + BKE_shaderfx_blend_read_data(reader, &ob->shader_fx); + + BLO_read_list(reader, &ob->effect); + paf = (PartEff *)ob->effect.first; + while (paf) { + if (paf->type == EFF_PARTICLE) { + paf->keys = nullptr; + } + if (paf->type == EFF_WAVE) { + WaveEff *wav = (WaveEff *)paf; + PartEff *next = paf->next; + WaveModifierData *wmd = (WaveModifierData *)BKE_modifier_new(eModifierType_Wave); + + wmd->damp = wav->damp; + wmd->flag = wav->flag; + wmd->height = wav->height; + wmd->lifetime = wav->lifetime; + wmd->narrow = wav->narrow; + wmd->speed = wav->speed; + wmd->startx = wav->startx; + wmd->starty = wav->startx; + wmd->timeoffs = wav->timeoffs; + wmd->width = wav->width; + + BLI_addtail(&ob->modifiers, wmd); + + BLI_remlink(&ob->effect, paf); + MEM_freeN(paf); + + paf = next; + continue; + } + if (paf->type == EFF_BUILD) { + BuildEff *baf = (BuildEff *)paf; + PartEff *next = paf->next; + BuildModifierData *bmd = (BuildModifierData *)BKE_modifier_new(eModifierType_Build); + + bmd->start = baf->sfra; + bmd->length = baf->len; + bmd->randomize = 0; + bmd->seed = 1; + + BLI_addtail(&ob->modifiers, bmd); + + BLI_remlink(&ob->effect, paf); + MEM_freeN(paf); + + paf = next; + continue; + } + paf = paf->next; + } + + BLO_read_data_address(reader, &ob->pd); + BKE_particle_partdeflect_blend_read_data(reader, ob->pd); + BLO_read_data_address(reader, &ob->soft); + if (ob->soft) { + SoftBody *sb = ob->soft; + + sb->bpoint = nullptr; /* init pointers so it gets rebuilt nicely */ + sb->bspring = nullptr; + sb->scratch = nullptr; + /* although not used anymore */ + /* still have to be loaded to be compatible with old files */ + BLO_read_pointer_array(reader, (void **)&sb->keys); + if (sb->keys) { + for (int a = 0; a < sb->totkey; a++) { + BLO_read_data_address(reader, &sb->keys[a]); + } + } + + BLO_read_data_address(reader, &sb->effector_weights); + if (!sb->effector_weights) { + sb->effector_weights = BKE_effector_add_weights(nullptr); + } + + BLO_read_data_address(reader, &sb->shared); + if (sb->shared == nullptr) { + /* Link deprecated caches if they exist, so we can use them for versioning. + * We should only do this when sb->shared == nullptr, because those pointers + * are always set (for compatibility with older Blenders). We mustn't link + * the same pointcache twice. */ + BKE_ptcache_blend_read_data(reader, &sb->ptcaches, &sb->pointcache, false); + } + else { + /* link caches */ + BKE_ptcache_blend_read_data(reader, &sb->shared->ptcaches, &sb->shared->pointcache, false); + } + } + BLO_read_data_address(reader, &ob->fluidsimSettings); /* NT */ + + BLO_read_data_address(reader, &ob->rigidbody_object); + if (ob->rigidbody_object) { + RigidBodyOb *rbo = ob->rigidbody_object; + /* Allocate runtime-only struct */ + rbo->shared = (RigidBodyOb_Shared *)MEM_callocN(sizeof(*rbo->shared), "RigidBodyObShared"); + } + BLO_read_data_address(reader, &ob->rigidbody_constraint); + if (ob->rigidbody_constraint) { + ob->rigidbody_constraint->physics_constraint = nullptr; + } + + BLO_read_list(reader, &ob->particlesystem); + BKE_particle_system_blend_read_data(reader, &ob->particlesystem); + + BKE_constraint_blend_read_data(reader, &ob->constraints); + + BLO_read_list(reader, &ob->hooks); + while (ob->hooks.first) { + ObHook *hook = (ObHook *)ob->hooks.first; + HookModifierData *hmd = (HookModifierData *)BKE_modifier_new(eModifierType_Hook); + + BLO_read_int32_array(reader, hook->totindex, &hook->indexar); + + /* Do conversion here because if we have loaded + * a hook we need to make sure it gets converted + * and freed, regardless of version. + */ + copy_v3_v3(hmd->cent, hook->cent); + hmd->falloff = hook->falloff; + hmd->force = hook->force; + hmd->indexar = hook->indexar; + hmd->object = hook->parent; + memcpy(hmd->parentinv, hook->parentinv, sizeof(hmd->parentinv)); + hmd->totindex = hook->totindex; + + BLI_addhead(&ob->modifiers, hmd); + BLI_remlink(&ob->hooks, hook); + + BKE_modifier_unique_name(&ob->modifiers, (ModifierData *)hmd); + + MEM_freeN(hook); + } + + BLO_read_data_address(reader, &ob->iuser); + if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE && !ob->iuser) { + BKE_object_empty_draw_type_set(ob, ob->empty_drawtype); + } + + BKE_object_runtime_reset(ob); + BLO_read_list(reader, &ob->pc_ids); + + /* in case this value changes in future, clamp else we get undefined behavior */ + CLAMP(ob->rotmode, ROT_MODE_MIN, ROT_MODE_MAX); + + if (ob->sculpt) { + ob->sculpt = nullptr; + /* Only create data on undo, otherwise rely on editor mode switching. */ + if (BLO_read_data_is_undo(reader) && (ob->mode & OB_MODE_ALL_SCULPT)) { + BKE_object_sculpt_data_create(ob); + } + } + + BLO_read_data_address(reader, &ob->preview); + BKE_previewimg_blend_read(reader, ob->preview); +} + +/* XXX deprecated - old animation system */ +static void lib_link_nlastrips(BlendLibReader *reader, ID *id, ListBase *striplist) +{ + LISTBASE_FOREACH (bActionStrip *, strip, striplist) { + BLO_read_id_address(reader, id->lib, &strip->object); + BLO_read_id_address(reader, id->lib, &strip->act); + BLO_read_id_address(reader, id->lib, &strip->ipo); + LISTBASE_FOREACH (bActionModifier *, amod, &strip->modifiers) { + BLO_read_id_address(reader, id->lib, &amod->ob); + } + } +} + +/* XXX deprecated - old animation system */ +static void lib_link_constraint_channels(BlendLibReader *reader, ID *id, ListBase *chanbase) +{ + LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) { + BLO_read_id_address(reader, id->lib, &chan->ipo); + } +} + +static void object_blend_read_lib(BlendLibReader *reader, ID *id) +{ + Object *ob = (Object *)id; + + Main *bmain = BLO_read_lib_get_main(reader); + BlendFileReadReport *reports = BLO_read_lib_reports(reader); + + /* XXX deprecated - old animation system <<< */ + BLO_read_id_address(reader, ob->id.lib, &ob->ipo); + BLO_read_id_address(reader, ob->id.lib, &ob->action); + /* >>> XXX deprecated - old animation system */ + + BLO_read_id_address(reader, ob->id.lib, &ob->parent); + BLO_read_id_address(reader, ob->id.lib, &ob->track); + BLO_read_id_address(reader, ob->id.lib, &ob->poselib); + + /* 2.8x drops support for non-empty dupli instances. */ + if (ob->type == OB_EMPTY) { + BLO_read_id_address(reader, ob->id.lib, &ob->instance_collection); + } + else { + if (ob->instance_collection != nullptr) { + ID *new_id = BLO_read_get_new_id_address(reader, ob->id.lib, &ob->instance_collection->id); + BLO_reportf_wrap(reports, + RPT_INFO, + TIP_("Non-Empty object '%s' cannot duplicate collection '%s' " + "anymore in Blender 2.80, removed instancing"), + ob->id.name + 2, + new_id->name + 2); + } + ob->instance_collection = nullptr; + ob->transflag &= ~OB_DUPLICOLLECTION; + } + + BLO_read_id_address(reader, ob->id.lib, &ob->proxy); + if (ob->proxy) { + /* paranoia check, actually a proxy_from pointer should never be written... */ + if (!ID_IS_LINKED(ob->proxy)) { + ob->proxy->proxy_from = nullptr; + ob->proxy = nullptr; + + if (ob->id.lib) { + BLO_reportf_wrap(reports, + RPT_INFO, + TIP_("Proxy lost from object %s lib %s\n"), + ob->id.name + 2, + ob->id.lib->filepath); + } + else { + BLO_reportf_wrap( + reports, RPT_INFO, TIP_("Proxy lost from object %s lib \n"), ob->id.name + 2); + } + reports->count.missing_obproxies++; + } + else { + /* this triggers object_update to always use a copy */ + ob->proxy->proxy_from = ob; + } + } + BLO_read_id_address(reader, ob->id.lib, &ob->proxy_group); + + void *poin = ob->data; + BLO_read_id_address(reader, ob->id.lib, &ob->data); + + if (ob->data == nullptr && poin != nullptr) { + ob->type = OB_EMPTY; + + if (ob->pose) { + /* we can't call #BKE_pose_free() here because of library linking + * freeing will recurse down into every pose constraints ID pointers + * which are not always valid, so for now free directly and suffer + * some leaked memory rather than crashing immediately + * while bad this _is_ an exceptional case - campbell */ +#if 0 + BKE_pose_free(ob->pose); +#else + MEM_freeN(ob->pose); +#endif + ob->pose = nullptr; + ob->mode &= ~OB_MODE_POSE; + } + + if (ob->id.lib) { + BLO_reportf_wrap(reports, + RPT_INFO, + TIP_("Can't find object data of %s lib %s\n"), + ob->id.name + 2, + ob->id.lib->filepath); + } + else { + BLO_reportf_wrap(reports, RPT_INFO, TIP_("Object %s lost data\n"), ob->id.name + 2); + } + reports->count.missing_obdata++; + } + for (int a = 0; a < ob->totcol; a++) { + BLO_read_id_address(reader, ob->id.lib, &ob->mat[a]); + } + + /* When the object is local and the data is library its possible + * the material list size gets out of sync. T22663. */ + if (ob->data && ob->id.lib != ((ID *)ob->data)->lib) { + BKE_object_materials_test(bmain, ob, (ID *)ob->data); + } + + BLO_read_id_address(reader, ob->id.lib, &ob->gpd); + + /* if id.us==0 a new base will be created later on */ + + /* WARNING! Also check expand_object(), should reflect the stuff below. */ + BKE_pose_blend_read_lib(reader, ob, ob->pose); + BKE_constraint_blend_read_lib(reader, &ob->id, &ob->constraints); + + /* XXX deprecated - old animation system <<< */ + lib_link_constraint_channels(reader, &ob->id, &ob->constraintChannels); + lib_link_nlastrips(reader, &ob->id, &ob->nlastrips); + /* >>> XXX deprecated - old animation system */ + + LISTBASE_FOREACH (PartEff *, paf, &ob->effect) { + if (paf->type == EFF_PARTICLE) { + BLO_read_id_address(reader, ob->id.lib, &paf->group); + } + } + + { + FluidsimModifierData *fluidmd = (FluidsimModifierData *)BKE_modifiers_findby_type( + ob, eModifierType_Fluidsim); + + if (fluidmd && fluidmd->fss) { + /* XXX: deprecated - old animation system. */ + BLO_read_id_address(reader, ob->id.lib, &fluidmd->fss->ipo); + } + } + + { + FluidModifierData *fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, + eModifierType_Fluid); + + if (fmd && (fmd->type == MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { + /* Flag for refreshing the simulation after loading */ + fmd->domain->flags |= FLUID_DOMAIN_FILE_LOAD; + } + else if (fmd && (fmd->type == MOD_FLUID_TYPE_FLOW) && fmd->flow) { + fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE; + } + else if (fmd && (fmd->type == MOD_FLUID_TYPE_EFFEC) && fmd->effector) { + fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE; + } + } + + /* texture field */ + if (ob->pd) { + BKE_particle_partdeflect_blend_read_lib(reader, &ob->id, ob->pd); + } + + if (ob->soft) { + BLO_read_id_address(reader, ob->id.lib, &ob->soft->collision_group); + + BLO_read_id_address(reader, ob->id.lib, &ob->soft->effector_weights->group); + } + + BKE_particle_system_blend_read_lib(reader, ob, &ob->id, &ob->particlesystem); + BKE_modifier_blend_read_lib(reader, ob); + BKE_gpencil_modifier_blend_read_lib(reader, ob); + BKE_shaderfx_blend_read_lib(reader, ob); + + if (ob->rigidbody_constraint) { + BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob1); + BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob2); + } +} + +/* XXX deprecated - old animation system */ +static void expand_constraint_channels(BlendExpander *expander, ListBase *chanbase) +{ + LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) { + BLO_expand(expander, chan->ipo); + } +} + +static void expand_object_expandModifiers(void *userData, + Object *UNUSED(ob), + ID **idpoin, + int UNUSED(cb_flag)) +{ + BlendExpander *expander = (BlendExpander *)userData; + BLO_expand(expander, *idpoin); +} + +PartEff *BKE_object_do_version_give_parteff_245(Object *ob) +{ + PartEff *paf; + + paf = (PartEff *)ob->effect.first; + while (paf) { + if (paf->type == EFF_PARTICLE) { + return paf; + } + paf = paf->next; + } + return nullptr; +} + +static void object_blend_read_expand(BlendExpander *expander, ID *id) +{ + Object *ob = (Object *)id; + + BLO_expand(expander, ob->data); + + BLO_expand(expander, ob->parent); + + /* expand_object_expandModifier() */ + if (ob->modifiers.first) { + BKE_modifiers_foreach_ID_link(ob, expand_object_expandModifiers, expander); + } + + /* expand_object_expandModifier() */ + if (ob->greasepencil_modifiers.first) { + BKE_gpencil_modifiers_foreach_ID_link(ob, expand_object_expandModifiers, expander); + } + + /* expand_object_expandShaderFx() */ + if (ob->shader_fx.first) { + BKE_shaderfx_foreach_ID_link(ob, expand_object_expandModifiers, expander); + } + + BKE_pose_blend_read_expand(expander, ob->pose); + BLO_expand(expander, ob->poselib); + BKE_constraint_blend_read_expand(expander, &ob->constraints); + + BLO_expand(expander, ob->gpd); + + /* XXX deprecated - old animation system (for version patching only) */ + BLO_expand(expander, ob->ipo); + BLO_expand(expander, ob->action); + + expand_constraint_channels(expander, &ob->constraintChannels); + + LISTBASE_FOREACH (bActionStrip *, strip, &ob->nlastrips) { + BLO_expand(expander, strip->object); + BLO_expand(expander, strip->act); + BLO_expand(expander, strip->ipo); + } + /* XXX deprecated - old animation system (for version patching only) */ + + for (int a = 0; a < ob->totcol; a++) { + BLO_expand(expander, ob->mat[a]); + } + + PartEff *paf = BKE_object_do_version_give_parteff_245(ob); + if (paf && paf->group) { + BLO_expand(expander, paf->group); + } + + if (ob->instance_collection) { + BLO_expand(expander, ob->instance_collection); + } + + if (ob->proxy) { + BLO_expand(expander, ob->proxy); + } + if (ob->proxy_group) { + BLO_expand(expander, ob->proxy_group); + } + + LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { + BLO_expand(expander, psys->part); + } + + if (ob->pd) { + BLO_expand(expander, ob->pd->tex); + BLO_expand(expander, ob->pd->f_source); + } + + if (ob->soft) { + BLO_expand(expander, ob->soft->collision_group); + + if (ob->soft->effector_weights) { + BLO_expand(expander, ob->soft->effector_weights->group); + } + } + + if (ob->rigidbody_constraint) { + BLO_expand(expander, ob->rigidbody_constraint->ob1); + BLO_expand(expander, ob->rigidbody_constraint->ob2); + } +} + +static void object_lib_override_apply_post(ID *id_dst, ID *id_src) +{ + /* id_dst is the new local override copy of the linked reference data. id_src is the old override + * data stored on disk, used as source data for override operations. */ + Object *object_dst = (Object *)id_dst; + Object *object_src = (Object *)id_src; + + ListBase pidlist_dst, pidlist_src; + BKE_ptcache_ids_from_object(&pidlist_dst, object_dst, nullptr, 0); + BKE_ptcache_ids_from_object(&pidlist_src, object_src, nullptr, 0); + + /* Problem with point caches is that several status flags (like OUTDATED or BAKED) are read-only + * at RNA level, and therefore not overridable per-se. + * + * This code is a workaround this to check all point-caches from both source and destination + * objects in parallel, and transfer those flags when it makes sense. + * + * This allows to keep baked caches across liboverrides applies. + * + * NOTE: This is fairly hackish and weak, but so is the point-cache system as its whole. A more + * robust solution would be e.g. to have a specific RNA entry point to deal with such cases + * (maybe a new flag to allow override code to set values of some read-only properties?). + */ + PTCacheID *pid_src, *pid_dst; + for (pid_dst = (PTCacheID *)pidlist_dst.first, pid_src = (PTCacheID *)pidlist_src.first; + pid_dst != nullptr; + pid_dst = pid_dst->next, pid_src = (pid_src != nullptr) ? pid_src->next : nullptr) { + /* If pid's do not match, just tag info of caches in dst as dirty and continue. */ + if (pid_src == nullptr) { + continue; + } + if (pid_dst->type != pid_src->type || pid_dst->file_type != pid_src->file_type || + pid_dst->default_step != pid_src->default_step || pid_dst->max_step != pid_src->max_step || + pid_dst->data_types != pid_src->data_types || pid_dst->info_types != pid_src->info_types) { + LISTBASE_FOREACH (PointCache *, point_cache_src, pid_src->ptcaches) { + point_cache_src->flag |= PTCACHE_FLAG_INFO_DIRTY; + } + continue; + } + + PointCache *point_cache_dst, *point_cache_src; + for (point_cache_dst = (PointCache *)pid_dst->ptcaches->first, + point_cache_src = (PointCache *)pid_src->ptcaches->first; + point_cache_dst != nullptr; + point_cache_dst = point_cache_dst->next, + point_cache_src = (point_cache_src != nullptr) ? point_cache_src->next : nullptr) { + /* Always force updating info about caches of applied liboverrides. */ + point_cache_dst->flag |= PTCACHE_FLAG_INFO_DIRTY; + if (point_cache_src == nullptr || !STREQ(point_cache_dst->name, point_cache_src->name)) { + continue; + } + if ((point_cache_src->flag & PTCACHE_BAKED) != 0) { + point_cache_dst->flag |= PTCACHE_BAKED; + } + if ((point_cache_src->flag & PTCACHE_OUTDATED) == 0) { + point_cache_dst->flag &= ~PTCACHE_OUTDATED; + } + } + } + BLI_freelistN(&pidlist_dst); + BLI_freelistN(&pidlist_src); +} + +static IDProperty *object_asset_dimensions_property(Object *ob) +{ + float dimensions[3]; + BKE_object_dimensions_get(ob, dimensions); + if (is_zero_v3(dimensions)) { + return nullptr; + } + + IDPropertyTemplate idprop = {0}; + idprop.array.len = ARRAY_SIZE(dimensions); + idprop.array.type = IDP_FLOAT; + + IDProperty *property = IDP_New(IDP_ARRAY, &idprop, "dimensions"); + memcpy(IDP_Array(property), dimensions, sizeof(dimensions)); + + return property; +} + +static void object_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data) +{ + Object *ob = (Object *)asset_ptr; + BLI_assert(GS(ob->id.name) == ID_OB); + + /* Update dimensions hint for the asset. */ + IDProperty *dimensions_prop = object_asset_dimensions_property(ob); + if (dimensions_prop) { + BKE_asset_metadata_idprop_ensure(asset_data, dimensions_prop); + } +} + +AssetTypeInfo AssetType_OB = { + /* pre_save_fn */ object_asset_pre_save, +}; + +IDTypeInfo IDType_ID_OB = { + /* id_code */ ID_OB, + /* id_filter */ FILTER_ID_OB, + /* main_listbase_index */ INDEX_ID_OB, + /* struct_size */ sizeof(Object), + /* name */ "Object", + /* name_plural */ "objects", + /* translation_context */ BLT_I18NCONTEXT_ID_OBJECT, + /* flags */ 0, + + /* init_data */ object_init_data, + /* copy_data */ object_copy_data, + /* free_data */ object_free_data, + /* make_local */ object_make_local, + /* foreach_id */ object_foreach_id, + /* foreach_cache */ nullptr, + /* owner_get */ nullptr, + + /* blend_write */ object_blend_write, + /* blend_read_data */ object_blend_read_data, + /* blend_read_lib */ object_blend_read_lib, + /* blend_read_expand */ object_blend_read_expand, + + /* blend_read_undo_preserve */ nullptr, + + /* lib_override_apply_post */ object_lib_override_apply_post, + + /* asset_type_info */ &AssetType_OB, +}; + +void BKE_object_workob_clear(Object *workob) +{ + memset(workob, 0, sizeof(Object)); + + workob->scale[0] = workob->scale[1] = workob->scale[2] = 1.0f; + workob->dscale[0] = workob->dscale[1] = workob->dscale[2] = 1.0f; + workob->rotmode = ROT_MODE_EUL; +} + +void BKE_object_free_particlesystems(Object *ob) +{ + ParticleSystem *psys; + + while ((psys = (ParticleSystem *)BLI_pophead(&ob->particlesystem))) { + psys_free(ob, psys); + } +} + +void BKE_object_free_softbody(Object *ob) +{ + sbFree(ob); +} + +void BKE_object_free_curve_cache(Object *ob) +{ + if (ob->runtime.curve_cache) { + BKE_displist_free(&ob->runtime.curve_cache->disp); + BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); + if (ob->runtime.curve_cache->anim_path_accum_length) { + MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length); + } + BKE_nurbList_free(&ob->runtime.curve_cache->deformed_nurbs); + MEM_freeN(ob->runtime.curve_cache); + ob->runtime.curve_cache = nullptr; + } +} + +void BKE_object_free_modifiers(Object *ob, const int flag) +{ + ModifierData *md; + GpencilModifierData *gp_md; + + while ((md = (ModifierData *)BLI_pophead(&ob->modifiers))) { + BKE_modifier_free_ex(md, flag); + } + + while ((gp_md = (GpencilModifierData *)BLI_pophead(&ob->greasepencil_modifiers))) { + BKE_gpencil_modifier_free_ex(gp_md, flag); + } + /* particle modifiers were freed, so free the particlesystems as well */ + BKE_object_free_particlesystems(ob); + + /* same for softbody */ + BKE_object_free_softbody(ob); + + /* modifiers may have stored data in the DM cache */ + BKE_object_free_derived_caches(ob); +} + +void BKE_object_free_shaderfx(Object *ob, const int flag) +{ + ShaderFxData *fx; + + while ((fx = (ShaderFxData *)BLI_pophead(&ob->shader_fx))) { + BKE_shaderfx_free_ex(fx, flag); + } +} + +void BKE_object_modifier_hook_reset(Object *ob, HookModifierData *hmd) +{ + /* reset functionality */ + if (hmd->object) { + bPoseChannel *pchan = BKE_pose_channel_find_name(hmd->object->pose, hmd->subtarget); + + if (hmd->subtarget[0] && pchan) { + float imat[4][4], mat[4][4]; + + /* Calculate the world-space matrix for the pose-channel target first, + * then carry on as usual. */ + mul_m4_m4m4(mat, hmd->object->obmat, pchan->pose_mat); + + invert_m4_m4(imat, mat); + mul_m4_m4m4(hmd->parentinv, imat, ob->obmat); + } + else { + invert_m4_m4(hmd->object->imat, hmd->object->obmat); + mul_m4_m4m4(hmd->parentinv, hmd->object->imat, ob->obmat); + } + } +} + +void BKE_object_modifier_gpencil_hook_reset(Object *ob, HookGpencilModifierData *hmd) +{ + if (hmd->object == nullptr) { + return; + } + /* reset functionality */ + bPoseChannel *pchan = BKE_pose_channel_find_name(hmd->object->pose, hmd->subtarget); + + if (hmd->subtarget[0] && pchan) { + float imat[4][4], mat[4][4]; + + /* Calculate the world-space matrix for the pose-channel target first, + * then carry on as usual. */ + mul_m4_m4m4(mat, hmd->object->obmat, pchan->pose_mat); + + invert_m4_m4(imat, mat); + mul_m4_m4m4(hmd->parentinv, imat, ob->obmat); + } + else { + invert_m4_m4(hmd->object->imat, hmd->object->obmat); + mul_m4_m4m4(hmd->parentinv, hmd->object->imat, ob->obmat); + } +} + +/** + * Set the object's active modifier. + * + * \param md: If nullptr, only clear the active modifier, otherwise + * it must be in the #Object.modifiers list. + */ +void BKE_object_modifier_set_active(Object *ob, ModifierData *md) +{ + LISTBASE_FOREACH (ModifierData *, md_iter, &ob->modifiers) { + md_iter->flag &= ~eModifierFlag_Active; + } + + if (md != nullptr) { + BLI_assert(BLI_findindex(&ob->modifiers, md) != -1); + md->flag |= eModifierFlag_Active; + } +} + +ModifierData *BKE_object_active_modifier(const Object *ob) +{ + /* In debug mode, check for only one active modifier. */ +#ifndef NDEBUG + int active_count = 0; + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->flag & eModifierFlag_Active) { + active_count++; + } + } + BLI_assert(ELEM(active_count, 0, 1)); +#endif + + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->flag & eModifierFlag_Active) { + return md; + } + } + + return nullptr; +} + +/** + * \return True if the object's type supports regular modifiers (not grease pencil modifiers). + */ +bool BKE_object_supports_modifiers(const Object *ob) +{ + return ( + ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE, OB_POINTCLOUD, OB_VOLUME)); +} + +bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) +{ + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)modifier_type); + + /* Surface and lattice objects don't output geometry sets. */ + if (mti->modifyGeometrySet != nullptr && ELEM(ob->type, OB_SURF, OB_LATTICE)) { + return false; + } + + /* Only geometry objects should be able to get modifiers T25291. */ + if (ob->type == OB_HAIR) { + return (mti->modifyHair != nullptr) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); + } + if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) { + return (mti->modifyGeometrySet != nullptr); + } + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { + if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) { + return false; + } + + if (!((mti->flags & eModifierTypeFlag_AcceptsCVs) || + (ob->type == OB_MESH && (mti->flags & eModifierTypeFlag_AcceptsMesh)))) { + return false; + } + + return true; + } + + return false; +} + +static bool object_modifier_type_copy_check(ModifierType md_type) +{ + return !ELEM(md_type, eModifierType_Hook, eModifierType_Collision); +} + +/** + * Find a `psys` matching given `psys_src` in `ob_dst` (i.e. sharing the same ParticleSettings ID), + * or add one, and return valid `psys` from `ob_dst`. + * + * \note Order handling is fairly weak here. This code assumes that it is called **before** the + * modifier using the psys is actually copied, and that this copied modifier will be added at the + * end of the stack. That way we can be sure that the particle modifier will be before the one + * using its particle system in the stack. + */ +static ParticleSystem *object_copy_modifier_particle_system_ensure(Main *bmain, + Scene *scene, + Object *ob_dst, + ParticleSystem *psys_src) +{ + ParticleSystem *psys_dst = nullptr; + + /* Check if a particle system with the same particle settings + * already exists on the destination object. */ + LISTBASE_FOREACH (ParticleSystem *, psys, &ob_dst->particlesystem) { + if (psys->part == psys_src->part) { + psys_dst = psys; + break; + } + } + + /* If it does not exist, copy the particle system to the destination object. */ + if (psys_dst == nullptr) { + ModifierData *md = object_copy_particle_system(bmain, scene, ob_dst, psys_src); + psys_dst = ((ParticleSystemModifierData *)md)->psys; + } + + return psys_dst; +} + +/** + * Copy a single modifier. + * + * \note **Do not** use this function to copy a whole modifier stack (see note below too). Use + * `BKE_object_modifier_stack_copy` instead. + * + * \note Complex modifiers relaying on other data (like e.g. dynamic paint or fluid using particle + * systems) are not always 100% 'correctly' copied here, since we have to use heuristics to decide + * which particle system to use or add in `ob_dst`, and it's placement in the stack, etc. If used + * more than once, this function should preferably be called in stack order. + */ +bool BKE_object_copy_modifier( + Main *bmain, Scene *scene, Object *ob_dst, const Object *ob_src, ModifierData *md_src) +{ + BLI_assert(ob_dst->type != OB_GPENCIL); + + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_src->type); + if (!object_modifier_type_copy_check((ModifierType)md_src->type)) { + /* We never allow copying those modifiers here. */ + return false; + } + if (!BKE_object_support_modifier_type_check(ob_dst, md_src->type)) { + return false; + } + if (mti->flags & eModifierTypeFlag_Single) { + if (BKE_modifiers_findby_type(ob_dst, (ModifierType)md_src->type) != nullptr) { + return false; + } + } + + ParticleSystem *psys_src = nullptr; + ParticleSystem *psys_dst = nullptr; + + switch (md_src->type) { + case eModifierType_Softbody: + BKE_object_copy_softbody(ob_dst, ob_src, 0); + break; + case eModifierType_Skin: + /* ensure skin-node customdata exists */ + BKE_mesh_ensure_skin_customdata((Mesh *)ob_dst->data); + break; + case eModifierType_Fluid: { + FluidModifierData *fmd = (FluidModifierData *)md_src; + if (fmd->type == MOD_FLUID_TYPE_FLOW) { + if (fmd->flow != nullptr && fmd->flow->psys != nullptr) { + psys_src = fmd->flow->psys; + psys_dst = object_copy_modifier_particle_system_ensure(bmain, scene, ob_dst, psys_src); + } + } + break; + } + case eModifierType_DynamicPaint: { + DynamicPaintModifierData *dpmd = (DynamicPaintModifierData *)md_src; + if (dpmd->brush != nullptr && dpmd->brush->psys != nullptr) { + psys_src = dpmd->brush->psys; + psys_dst = object_copy_modifier_particle_system_ensure(bmain, scene, ob_dst, psys_src); + } + break; + } + default: + break; + } + + ModifierData *md_dst; + if (md_src->type == eModifierType_ParticleSystem) { + md_dst = object_copy_particle_system( + bmain, scene, ob_dst, ((ParticleSystemModifierData *)md_src)->psys); + } + else { + md_dst = BKE_modifier_new(md_src->type); + + BLI_strncpy(md_dst->name, md_src->name, sizeof(md_dst->name)); + + if (md_src->type == eModifierType_Multires) { + /* Has to be done after mod creation, but *before* we actually copy its settings! */ + multiresModifier_sync_levels_ex( + ob_dst, (MultiresModifierData *)md_src, (MultiresModifierData *)md_dst); + } + + BKE_modifier_copydata(md_src, md_dst); + + switch (md_dst->type) { + case eModifierType_Fluid: + if (psys_dst != nullptr) { + FluidModifierData *fmd_dst = (FluidModifierData *)md_dst; + BLI_assert(fmd_dst->type == MOD_FLUID_TYPE_FLOW && fmd_dst->flow != nullptr && + fmd_dst->flow->psys != nullptr); + fmd_dst->flow->psys = psys_dst; + } + break; + case eModifierType_DynamicPaint: + if (psys_dst != nullptr) { + DynamicPaintModifierData *dpmd_dst = (DynamicPaintModifierData *)md_dst; + BLI_assert(dpmd_dst->brush != nullptr && dpmd_dst->brush->psys != nullptr); + dpmd_dst->brush->psys = psys_dst; + } + break; + default: + break; + } + + BLI_addtail(&ob_dst->modifiers, md_dst); + BKE_modifier_unique_name(&ob_dst->modifiers, md_dst); + } + + BKE_object_modifier_set_active(ob_dst, md_dst); + + return true; +} + +/** + * Copy a single GPencil modifier. + * + * \note **Do not** use this function to copy a whole modifier stack. Use + * `BKE_object_modifier_stack_copy` instead. + */ +bool BKE_object_copy_gpencil_modifier(struct Object *ob_dst, GpencilModifierData *gmd_src) +{ + BLI_assert(ob_dst->type == OB_GPENCIL); + + GpencilModifierData *gmd_dst = BKE_gpencil_modifier_new(gmd_src->type); + BLI_strncpy(gmd_dst->name, gmd_src->name, sizeof(gmd_dst->name)); + + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info( + (GpencilModifierType)gmd_src->type); + mti->copyData(gmd_src, gmd_dst); + + BLI_addtail(&ob_dst->greasepencil_modifiers, gmd_dst); + BKE_gpencil_modifier_unique_name(&ob_dst->greasepencil_modifiers, gmd_dst); + + return true; +} + +/** + * Copy the whole stack of modifiers from one object into another. + * + * \warning **Does not** clear modifier stack and related data (particle systems, soft-body, + * etc.) in `ob_dst`, if needed calling code must do it. + * + * \param do_copy_all: If true, even modifiers that should not support copying (like Hook one) + * will be duplicated. + */ +bool BKE_object_modifier_stack_copy(Object *ob_dst, + const Object *ob_src, + const bool do_copy_all, + const int flag_subdata) +{ + if ((ob_dst->type == OB_GPENCIL) != (ob_src->type == OB_GPENCIL)) { + BLI_assert_msg(0, + "Trying to copy a modifier stack between a GPencil object and another type."); + return false; + } + + if (!BLI_listbase_is_empty(&ob_dst->modifiers) || + !BLI_listbase_is_empty(&ob_dst->greasepencil_modifiers)) { + BLI_assert( + !"Trying to copy a modifier stack into an object having a non-empty modifier stack."); + return false; + } + + LISTBASE_FOREACH (ModifierData *, md_src, &ob_src->modifiers) { + if (!do_copy_all && !object_modifier_type_copy_check((ModifierType)md_src->type)) { + continue; + } + if (!BKE_object_support_modifier_type_check(ob_dst, md_src->type)) { + continue; + } + + ModifierData *md_dst = BKE_modifier_new(md_src->type); + BLI_strncpy(md_dst->name, md_src->name, sizeof(md_dst->name)); + BKE_modifier_copydata_ex(md_src, md_dst, flag_subdata); + BLI_addtail(&ob_dst->modifiers, md_dst); + } + + LISTBASE_FOREACH (GpencilModifierData *, gmd_src, &ob_src->greasepencil_modifiers) { + GpencilModifierData *gmd_dst = BKE_gpencil_modifier_new(gmd_src->type); + BLI_strncpy(gmd_dst->name, gmd_src->name, sizeof(gmd_dst->name)); + BKE_gpencil_modifier_copydata_ex(gmd_src, gmd_dst, flag_subdata); + BLI_addtail(&ob_dst->greasepencil_modifiers, gmd_dst); + } + + /* This could be copied from anywhere, since no other modifier actually use this data. But for + * consistency do it together with particle systems. */ + BKE_object_copy_softbody(ob_dst, ob_src, flag_subdata); + + /* It is mandatory that this happens after copying modifiers, as it will update their `psys` + * pointers accordingly. */ + BKE_object_copy_particlesystems(ob_dst, ob_src, flag_subdata); + + return true; +} + +void BKE_object_link_modifiers(Object *ob_dst, const Object *ob_src) +{ + BKE_object_free_modifiers(ob_dst, 0); + + BKE_object_modifier_stack_copy(ob_dst, ob_src, false, 0); +} + +/** + * Copy CCG related data. Used to sync copy of mesh with reshaped original mesh. + */ +static void copy_ccg_data(Mesh *mesh_destination, Mesh *mesh_source, int layer_type) +{ + BLI_assert(mesh_destination->totloop == mesh_source->totloop); + CustomData *data_destination = &mesh_destination->ldata; + CustomData *data_source = &mesh_source->ldata; + const int num_elements = mesh_source->totloop; + if (!CustomData_has_layer(data_source, layer_type)) { + return; + } + const int layer_index = CustomData_get_layer_index(data_destination, layer_type); + CustomData_free_layer(data_destination, layer_type, num_elements, layer_index); + BLI_assert(!CustomData_has_layer(data_destination, layer_type)); + CustomData_add_layer(data_destination, layer_type, CD_CALLOC, nullptr, num_elements); + BLI_assert(CustomData_has_layer(data_destination, layer_type)); + CustomData_copy_layer_type_data(data_source, data_destination, layer_type, 0, 0, num_elements); +} + +static void object_update_from_subsurf_ccg(Object *object) +{ + /* Currently CCG is only created for Mesh objects. */ + if (object->type != OB_MESH) { + return; + } + /* If object does not own evaluated mesh we can not access it since it might be freed already + * (happens on dependency graph free where order of CoW-ed IDs free is undefined). + * + * Good news is: such mesh does not have modifiers applied, so no need to worry about CCG. */ + if (!object->runtime.is_data_eval_owned) { + return; + } + /* Object was never evaluated, so can not have CCG subdivision surface. */ + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object); + if (mesh_eval == nullptr) { + return; + } + SubdivCCG *subdiv_ccg = mesh_eval->runtime.subdiv_ccg; + if (subdiv_ccg == nullptr) { + return; + } + /* Check whether there is anything to be reshaped. */ + if (!subdiv_ccg->dirty.coords && !subdiv_ccg->dirty.hidden) { + return; + } + const int tot_level = mesh_eval->runtime.subdiv_ccg_tot_level; + Object *object_orig = DEG_get_original_object(object); + Mesh *mesh_orig = (Mesh *)object_orig->data; + multiresModifier_reshapeFromCCG(tot_level, mesh_orig, subdiv_ccg); + /* NOTE: we need to reshape into an original mesh from main database, + * allowing: + * + * - Update copies of that mesh at any moment. + * - Save the file without doing extra reshape. + * - All the users of the mesh have updated displacement. + * + * However, the tricky part here is that we only know about sculpted + * state of a mesh on an object level, and object is being updated after + * mesh datablock is updated. This forces us to: + * + * - Update mesh datablock from object evaluation, which is technically + * forbidden, but there is no other place for this yet. + * - Reshape to the original mesh from main database, and then copy updated + * layer to copy of that mesh (since copy of the mesh has decoupled + * custom data layers). + * + * All this is defeating all the designs we need to follow to allow safe + * threaded evaluation, but this is as good as we can make it within the + * current sculpt/evaluated mesh design. This is also how we've survived + * with old DerivedMesh based solutions. So, while this is all wrong and + * needs reconsideration, doesn't seem to be a big stopper for real + * production artists. + */ + /* TODO(sergey): Solve this somehow, to be fully stable for threaded + * evaluation environment. + */ + /* NOTE: runtime.data_orig is what was before assigning mesh_eval, + * it is orig as in what was in object_eval->data before evaluating + * modifier stack. + * + * mesh_cow is a copy-on-written version od object_orig->data. + */ + Mesh *mesh_cow = (Mesh *)object->runtime.data_orig; + copy_ccg_data(mesh_cow, mesh_orig, CD_MDISPS); + copy_ccg_data(mesh_cow, mesh_orig, CD_GRID_PAINT_MASK); + /* Everything is now up-to-date. */ + subdiv_ccg->dirty.coords = false; + subdiv_ccg->dirty.hidden = false; +} + +/** + * Assign #Object.data after modifier stack evaluation. + */ +void BKE_object_eval_assign_data(Object *object_eval, ID *data_eval, bool is_owned) +{ + BLI_assert(object_eval->id.tag & LIB_TAG_COPIED_ON_WRITE); + BLI_assert(object_eval->runtime.data_eval == nullptr); + BLI_assert(data_eval->tag & LIB_TAG_NO_MAIN); + + if (is_owned) { + /* Set flag for debugging. */ + data_eval->tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT; + } + + /* Assigned evaluated data. */ + object_eval->runtime.data_eval = data_eval; + object_eval->runtime.is_data_eval_owned = is_owned; + + /* Overwrite data of evaluated object, if the datablock types match. */ + ID *data = (ID *)object_eval->data; + if (GS(data->name) == GS(data_eval->name)) { + /* NOTE: we are not supposed to invoke evaluation for original objects, + * but some areas are still being ported, so we play safe here. */ + if (object_eval->id.tag & LIB_TAG_COPIED_ON_WRITE) { + object_eval->data = data_eval; + } + } + + /* Is set separately currently. */ + object_eval->runtime.geometry_set_eval = nullptr; +} + +/** + * Free data derived from mesh, called when mesh changes or is freed. + */ +void BKE_object_free_derived_caches(Object *ob) +{ + MEM_SAFE_FREE(ob->runtime.bb); + + object_update_from_subsurf_ccg(ob); + + if (ob->runtime.data_eval != nullptr) { + if (ob->runtime.is_data_eval_owned) { + ID *data_eval = ob->runtime.data_eval; + if (GS(data_eval->name) == ID_ME) { + BKE_mesh_eval_delete((Mesh *)data_eval); + } + else { + BKE_libblock_free_datablock(data_eval, 0); + MEM_freeN(data_eval); + } + } + ob->runtime.data_eval = nullptr; + } + if (ob->runtime.mesh_deform_eval != nullptr) { + Mesh *mesh_deform_eval = ob->runtime.mesh_deform_eval; + BKE_mesh_eval_delete(mesh_deform_eval); + ob->runtime.mesh_deform_eval = nullptr; + } + + /* Restore initial pointer for copy-on-write datablocks, object->data + * might be pointing to an evaluated datablock data was just freed above. */ + if (ob->runtime.data_orig != nullptr) { + ob->data = ob->runtime.data_orig; + } + + BKE_object_to_mesh_clear(ob); + BKE_object_to_curve_clear(ob); + BKE_object_free_curve_cache(ob); + + /* Clear grease pencil data. */ + if (ob->runtime.gpd_eval != nullptr) { + BKE_gpencil_eval_delete(ob->runtime.gpd_eval); + ob->runtime.gpd_eval = nullptr; + } + + if (ob->runtime.geometry_set_eval != nullptr) { + BKE_geometry_set_free(ob->runtime.geometry_set_eval); + ob->runtime.geometry_set_eval = nullptr; + } +} + +void BKE_object_free_caches(Object *object) +{ + short update_flag = 0; + + /* Free particle system caches holding paths. */ + if (object->particlesystem.first) { + LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { + psys_free_path_cache(psys, psys->edit); + update_flag |= ID_RECALC_PSYS_REDO; + } + } + + /* Free memory used by cached derived meshes in the particle system modifiers. */ + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_ParticleSystem) { + ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; + if (psmd->mesh_final) { + BKE_id_free(nullptr, psmd->mesh_final); + psmd->mesh_final = nullptr; + if (psmd->mesh_original) { + BKE_id_free(nullptr, psmd->mesh_original); + psmd->mesh_original = nullptr; + } + psmd->flag |= eParticleSystemFlag_file_loaded; + update_flag |= ID_RECALC_GEOMETRY; + } + } + } + + /* NOTE: If object is coming from a duplicator, it might be a temporary + * object created by dependency graph, which shares pointers with original + * object. In this case we can not free anything. + */ + if ((object->base_flag & BASE_FROM_DUPLI) == 0) { + BKE_object_free_derived_caches(object); + update_flag |= ID_RECALC_GEOMETRY; + } + + /* Tag object for update, so once memory critical operation is over and + * scene update routines are back to its business the object will be + * guaranteed to be in a known state. + */ + if (update_flag != 0) { + DEG_id_tag_update(&object->id, update_flag); + } +} + +/** + * Actual check for internal data, not context or flags. + */ +bool BKE_object_is_in_editmode(const Object *ob) +{ + if (ob->data == nullptr) { + return false; + } + + switch (ob->type) { + case OB_MESH: + return ((Mesh *)ob->data)->edit_mesh != nullptr; + case OB_ARMATURE: + return ((bArmature *)ob->data)->edbo != nullptr; + case OB_FONT: + return ((Curve *)ob->data)->editfont != nullptr; + case OB_MBALL: + return ((MetaBall *)ob->data)->editelems != nullptr; + case OB_LATTICE: + return ((Lattice *)ob->data)->editlatt != nullptr; + case OB_SURF: + case OB_CURVE: + return ((Curve *)ob->data)->editnurb != nullptr; + case OB_GPENCIL: + /* Grease Pencil object has no edit mode data. */ + return GPENCIL_EDIT_MODE((bGPdata *)ob->data); + default: + return false; + } +} + +bool BKE_object_is_in_editmode_vgroup(const Object *ob) +{ + return (OB_TYPE_SUPPORT_VGROUP(ob->type) && BKE_object_is_in_editmode(ob)); +} + +bool BKE_object_data_is_in_editmode(const ID *id) +{ + const short type = GS(id->name); + BLI_assert(OB_DATA_SUPPORT_EDITMODE(type)); + switch (type) { + case ID_ME: + return ((const Mesh *)id)->edit_mesh != nullptr; + case ID_CU: + return ((((const Curve *)id)->editnurb != nullptr) || + (((const Curve *)id)->editfont != nullptr)); + case ID_MB: + return ((const MetaBall *)id)->editelems != nullptr; + case ID_LT: + return ((const Lattice *)id)->editlatt != nullptr; + case ID_AR: + return ((const bArmature *)id)->edbo != nullptr; + default: + BLI_assert_unreachable(); + return false; + } +} + +char *BKE_object_data_editmode_flush_ptr_get(struct ID *id) +{ + const short type = GS(id->name); + switch (type) { + case ID_ME: { + BMEditMesh *em = ((Mesh *)id)->edit_mesh; + if (em != nullptr) { + return &em->needs_flush_to_id; + } + break; + } + case ID_CU: { + if (((Curve *)id)->vfont != nullptr) { + EditFont *ef = ((Curve *)id)->editfont; + if (ef != nullptr) { + return &ef->needs_flush_to_id; + } + } + else { + EditNurb *editnurb = ((Curve *)id)->editnurb; + if (editnurb) { + return &editnurb->needs_flush_to_id; + } + } + break; + } + case ID_MB: { + MetaBall *mb = (MetaBall *)id; + return &mb->needs_flush_to_id; + } + case ID_LT: { + EditLatt *editlatt = ((Lattice *)id)->editlatt; + if (editlatt) { + return &editlatt->needs_flush_to_id; + } + break; + } + case ID_AR: { + bArmature *arm = (bArmature *)id; + return &arm->needs_flush_to_id; + } + default: + BLI_assert_unreachable(); + return nullptr; + } + return nullptr; +} + +bool BKE_object_is_in_wpaint_select_vert(const Object *ob) +{ + if (ob->type == OB_MESH) { + Mesh *me = (Mesh *)ob->data; + return ((ob->mode & OB_MODE_WEIGHT_PAINT) && (me->edit_mesh == nullptr) && + (ME_EDIT_PAINT_SEL_MODE(me) == SCE_SELECT_VERTEX)); + } + + return false; +} + +bool BKE_object_has_mode_data(const struct Object *ob, eObjectMode object_mode) +{ + if (object_mode & OB_MODE_EDIT) { + if (BKE_object_is_in_editmode(ob)) { + return true; + } + } + else if (object_mode & OB_MODE_VERTEX_PAINT) { + if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT)) { + return true; + } + } + else if (object_mode & OB_MODE_WEIGHT_PAINT) { + if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT)) { + return true; + } + } + else if (object_mode & OB_MODE_SCULPT) { + if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) { + return true; + } + } + else if (object_mode & OB_MODE_POSE) { + if (ob->pose != nullptr) { + return true; + } + } + return false; +} + +bool BKE_object_is_mode_compat(const struct Object *ob, eObjectMode object_mode) +{ + return ((ob->mode == object_mode) || (ob->mode & object_mode) != 0); +} + +/** + * Return which parts of the object are visible, as evaluated by depsgraph + */ +int BKE_object_visibility(const Object *ob, const int dag_eval_mode) +{ + if ((ob->base_flag & BASE_VISIBLE_DEPSGRAPH) == 0) { + return 0; + } + + /* Test which components the object has. */ + int visibility = OB_VISIBLE_SELF; + if (ob->particlesystem.first) { + visibility |= OB_VISIBLE_INSTANCES | OB_VISIBLE_PARTICLES; + } + else if (ob->transflag & OB_DUPLI) { + visibility |= OB_VISIBLE_INSTANCES; + } + + if (BKE_object_has_geometry_set_instances(ob)) { + visibility |= OB_VISIBLE_INSTANCES; + } + + /* Optional hiding of self if there are particles or instancers. */ + if (visibility & (OB_VISIBLE_PARTICLES | OB_VISIBLE_INSTANCES)) { + switch ((eEvaluationMode)dag_eval_mode) { + case DAG_EVAL_VIEWPORT: + if (!(ob->duplicator_visibility_flag & OB_DUPLI_FLAG_VIEWPORT)) { + visibility &= ~OB_VISIBLE_SELF; + } + break; + case DAG_EVAL_RENDER: + if (!(ob->duplicator_visibility_flag & OB_DUPLI_FLAG_RENDER)) { + visibility &= ~OB_VISIBLE_SELF; + } + break; + } + } + + return visibility; +} + +bool BKE_object_exists_check(Main *bmain, const Object *obtest) +{ + if (obtest == nullptr) { + return false; + } + + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob == obtest) { + return true; + } + } + + return false; +} + +/* *************************************************** */ + +static const char *get_obdata_defname(int type) +{ + switch (type) { + case OB_MESH: + return DATA_("Mesh"); + case OB_CURVE: + return DATA_("Curve"); + case OB_SURF: + return DATA_("Surf"); + case OB_FONT: + return DATA_("Text"); + case OB_MBALL: + return DATA_("Mball"); + case OB_CAMERA: + return DATA_("Camera"); + case OB_LAMP: + return CTX_DATA_(BLT_I18NCONTEXT_ID_LIGHT, "Light"); + case OB_LATTICE: + return DATA_("Lattice"); + case OB_ARMATURE: + return DATA_("Armature"); + case OB_SPEAKER: + return DATA_("Speaker"); + case OB_HAIR: + return DATA_("Hair"); + case OB_POINTCLOUD: + return DATA_("PointCloud"); + case OB_VOLUME: + return DATA_("Volume"); + case OB_EMPTY: + return DATA_("Empty"); + case OB_GPENCIL: + return DATA_("GPencil"); + case OB_LIGHTPROBE: + return DATA_("LightProbe"); + default: + CLOG_ERROR(&LOG, "Internal error, bad type: %d", type); + return DATA_("Empty"); + } +} + +static void object_init(Object *ob, const short ob_type) +{ + object_init_data(&ob->id); + + ob->type = ob_type; + + if (ob->type != OB_EMPTY) { + zero_v2(ob->ima_ofs); + } + + if (ELEM(ob->type, OB_LAMP, OB_CAMERA, OB_SPEAKER)) { + ob->trackflag = OB_NEGZ; + ob->upflag = OB_POSY; + } + + if (ob->type == OB_GPENCIL) { + ob->dtx |= OB_USE_GPENCIL_LIGHTS; + } + + if (ob->type == OB_LAMP) { + /* Lights are invisible to camera rays and are assumed to be a + * shadow catcher by default. */ + ob->visibility_flag |= OB_HIDE_CAMERA | OB_SHADOW_CATCHER; + } +} + +void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) +{ + if (name == nullptr) { + name = get_obdata_defname(type); + } + + switch (type) { + case OB_MESH: + return BKE_mesh_add(bmain, name); + case OB_CURVE: + return BKE_curve_add(bmain, name, OB_CURVE); + case OB_SURF: + return BKE_curve_add(bmain, name, OB_SURF); + case OB_FONT: + return BKE_curve_add(bmain, name, OB_FONT); + case OB_MBALL: + return BKE_mball_add(bmain, name); + case OB_CAMERA: + return BKE_camera_add(bmain, name); + case OB_LAMP: + return BKE_light_add(bmain, name); + case OB_LATTICE: + return BKE_lattice_add(bmain, name); + case OB_ARMATURE: + return BKE_armature_add(bmain, name); + case OB_SPEAKER: + return BKE_speaker_add(bmain, name); + case OB_LIGHTPROBE: + return BKE_lightprobe_add(bmain, name); + case OB_GPENCIL: + return BKE_gpencil_data_addnew(bmain, name); + case OB_HAIR: + return BKE_hair_add(bmain, name); + case OB_POINTCLOUD: + return BKE_pointcloud_add_default(bmain, name); + case OB_VOLUME: + return BKE_volume_add(bmain, name); + case OB_EMPTY: + return nullptr; + default: + CLOG_ERROR(&LOG, "Internal error, bad type: %d", type); + return nullptr; + } +} + +/** + * Return -1 on failure. + */ +int BKE_object_obdata_to_type(const ID *id) +{ + /* Keep in sync with #OB_DATA_SUPPORT_ID macro. */ + switch (GS(id->name)) { + case ID_ME: + return OB_MESH; + case ID_CU: + return BKE_curve_type_get((const Curve *)id); + case ID_MB: + return OB_MBALL; + case ID_LA: + return OB_LAMP; + case ID_SPK: + return OB_SPEAKER; + case ID_CA: + return OB_CAMERA; + case ID_LT: + return OB_LATTICE; + case ID_GD: + return OB_GPENCIL; + case ID_AR: + return OB_ARMATURE; + case ID_LP: + return OB_LIGHTPROBE; + case ID_HA: + return OB_HAIR; + case ID_PT: + return OB_POINTCLOUD; + case ID_VO: + return OB_VOLUME; + default: + return -1; + } +} + +/** + * More general add: creates minimum required data, but without vertices etc. + */ +Object *BKE_object_add_only_object(Main *bmain, int type, const char *name) +{ + if (!name) { + name = get_obdata_defname(type); + } + + /* We cannot use #BKE_id_new here as we need some custom initialization code. */ + Object *ob = (Object *)BKE_libblock_alloc(bmain, ID_OB, name, 0); + + /* We increase object user count when linking to Collections. */ + id_us_min(&ob->id); + + /* default object vars */ + object_init(ob, type); + + return ob; +} + +static Object *object_add_common(Main *bmain, ViewLayer *view_layer, int type, const char *name) +{ + Object *ob = BKE_object_add_only_object(bmain, type, name); + ob->data = BKE_object_obdata_add_from_type(bmain, type, name); + BKE_view_layer_base_deselect_all(view_layer); + + DEG_id_tag_update_ex( + bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + return ob; +} + +/** + * General add: to scene, with layer from area and default name + * + * Object is added to the active #Collection. + * If there is no linked collection to the active #ViewLayer we create a new one. + * + * \note Creates minimum required data, but without vertices etc. + */ +Object *BKE_object_add(Main *bmain, ViewLayer *view_layer, int type, const char *name) +{ + Object *ob = object_add_common(bmain, view_layer, type, name); + + LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + + Base *base = BKE_view_layer_base_find(view_layer, ob); + BKE_view_layer_base_select_and_set_active(view_layer, base); + + return ob; +} + +/** + * Add a new object, using another one as a reference + * + * \param ob_src: object to use to determine the collections of the new object. + */ +Object *BKE_object_add_from( + Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name, Object *ob_src) +{ + Object *ob = object_add_common(bmain, view_layer, type, name); + BKE_collection_object_add_from(bmain, scene, ob_src, ob); + + Base *base = BKE_view_layer_base_find(view_layer, ob); + BKE_view_layer_base_select_and_set_active(view_layer, base); + + return ob; +} + +/** + * Add a new object, but assign the given datablock as the ob->data + * for the newly created object. + * + * \param data: The datablock to assign as ob->data for the new object. + * This is assumed to be of the correct type. + * \param do_id_user: If true, id_us_plus() will be called on data when + * assigning it to the object. + */ +Object *BKE_object_add_for_data( + Main *bmain, ViewLayer *view_layer, int type, const char *name, ID *data, bool do_id_user) +{ + /* same as object_add_common, except we don't create new ob->data */ + Object *ob = BKE_object_add_only_object(bmain, type, name); + ob->data = (void *)data; + if (do_id_user) { + id_us_plus(data); + } + + BKE_view_layer_base_deselect_all(view_layer); + DEG_id_tag_update_ex( + bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + + LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + + Base *base = BKE_view_layer_base_find(view_layer, ob); + BKE_view_layer_base_select_and_set_active(view_layer, base); + + return ob; +} + +void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int flag) +{ + SoftBody *sb = ob_src->soft; + const bool is_orig = (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0; + + ob_dst->softflag = ob_src->softflag; + if (sb == nullptr) { + ob_dst->soft = nullptr; + return; + } + + SoftBody *sbn = (SoftBody *)MEM_dupallocN(sb); + + if ((flag & LIB_ID_COPY_CACHES) == 0) { + sbn->totspring = sbn->totpoint = 0; + sbn->bpoint = nullptr; + sbn->bspring = nullptr; + } + else { + sbn->totspring = sb->totspring; + sbn->totpoint = sb->totpoint; + + if (sbn->bpoint) { + int i; + + sbn->bpoint = (BodyPoint *)MEM_dupallocN(sbn->bpoint); + + for (i = 0; i < sbn->totpoint; i++) { + if (sbn->bpoint[i].springs) { + sbn->bpoint[i].springs = (int *)MEM_dupallocN(sbn->bpoint[i].springs); + } + } + } + + if (sb->bspring) { + sbn->bspring = (struct BodySpring *)MEM_dupallocN(sb->bspring); + } + } + + sbn->keys = nullptr; + sbn->totkey = sbn->totpointkey = 0; + + sbn->scratch = nullptr; + + if (is_orig) { + sbn->shared = (SoftBody_Shared *)MEM_dupallocN(sb->shared); + sbn->shared->pointcache = BKE_ptcache_copy_list( + &sbn->shared->ptcaches, &sb->shared->ptcaches, flag); + } + + if (sb->effector_weights) { + sbn->effector_weights = (EffectorWeights *)MEM_dupallocN(sb->effector_weights); + } + + ob_dst->soft = sbn; +} + +ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int flag) +{ + ParticleSystem *psysn = (ParticleSystem *)MEM_dupallocN(psys); + + psys_copy_particles(psysn, psys); + + if (psys->clmd) { + psysn->clmd = (ClothModifierData *)BKE_modifier_new(eModifierType_Cloth); + BKE_modifier_copydata_ex((ModifierData *)psys->clmd, (ModifierData *)psysn->clmd, flag); + psys->hair_in_mesh = psys->hair_out_mesh = nullptr; + } + + BLI_duplicatelist(&psysn->targets, &psys->targets); + + psysn->pathcache = nullptr; + psysn->childcache = nullptr; + psysn->edit = nullptr; + psysn->pdd = nullptr; + psysn->effectors = nullptr; + psysn->tree = nullptr; + psysn->bvhtree = nullptr; + psysn->batch_cache = nullptr; + + BLI_listbase_clear(&psysn->pathcachebufs); + BLI_listbase_clear(&psysn->childcachebufs); + + if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { + /* XXX Disabled, fails when evaluating depsgraph after copying ID with no main for preview + * creation. */ + // BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0); + psysn->flag |= PSYS_SHARED_CACHES; + BLI_assert(psysn->pointcache != nullptr); + } + else { + psysn->pointcache = BKE_ptcache_copy_list(&psysn->ptcaches, &psys->ptcaches, flag); + } + + /* XXX(@campbellbarton): from reading existing code this seems correct but intended usage of + * point-cache should /w cloth should be added in 'ParticleSystem'. */ + if (psysn->clmd) { + psysn->clmd->point_cache = psysn->pointcache; + } + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + id_us_plus((ID *)psysn->part); + } + + return psysn; +} + +void BKE_object_copy_particlesystems(Object *ob_dst, const Object *ob_src, const int flag) +{ + if (ob_dst->type != OB_MESH) { + /* currently only mesh objects can have soft body */ + return; + } + + BLI_listbase_clear(&ob_dst->particlesystem); + LISTBASE_FOREACH (ParticleSystem *, psys, &ob_src->particlesystem) { + ParticleSystem *npsys = BKE_object_copy_particlesystem(psys, flag); + + BLI_addtail(&ob_dst->particlesystem, npsys); + + /* need to update particle modifiers too */ + LISTBASE_FOREACH (ModifierData *, md, &ob_dst->modifiers) { + if (md->type == eModifierType_ParticleSystem) { + ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; + if (psmd->psys == psys) { + psmd->psys = npsys; + } + } + else if (md->type == eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; + if (pmd->brush) { + if (pmd->brush->psys == psys) { + pmd->brush->psys = npsys; + } + } + } + else if (md->type == eModifierType_Fluid) { + FluidModifierData *fmd = (FluidModifierData *)md; + + if (fmd->type == MOD_FLUID_TYPE_FLOW) { + if (fmd->flow) { + if (fmd->flow->psys == psys) { + fmd->flow->psys = npsys; + } + } + } + } + } + } +} + +static void copy_object_pose(Object *obn, const Object *ob, const int flag) +{ + /* NOTE: need to clear obn->pose pointer first, + * so that BKE_pose_copy_data works (otherwise there's a crash) */ + obn->pose = nullptr; + BKE_pose_copy_data_ex(&obn->pose, ob->pose, flag, true); /* true = copy constraints */ + + LISTBASE_FOREACH (bPoseChannel *, chan, &obn->pose->chanbase) { + chan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE); + + /* XXX Remapping object pointing onto itself should be handled by generic + * BKE_library_remap stuff, but... + * the flush_constraint_targets callback am not sure about, so will delay that for now. */ + LISTBASE_FOREACH (bConstraint *, con, &chan->constraints) { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + ListBase targets = {nullptr, nullptr}; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) { + if (ct->tar == ob) { + ct->tar = obn; + } + } + + if (cti->flush_constraint_targets) { + cti->flush_constraint_targets(con, &targets, false); + } + } + } + } +} + +bool BKE_object_pose_context_check(const Object *ob) +{ + if ((ob) && (ob->type == OB_ARMATURE) && (ob->pose) && (ob->mode & OB_MODE_POSE)) { + return true; + } + + return false; +} + +Object *BKE_object_pose_armature_get(Object *ob) +{ + if (ob == nullptr) { + return nullptr; + } + + if (BKE_object_pose_context_check(ob)) { + return ob; + } + + ob = BKE_modifiers_is_deformed_by_armature(ob); + + /* Only use selected check when non-active. */ + if (BKE_object_pose_context_check(ob)) { + return ob; + } + + return nullptr; +} + +Object *BKE_object_pose_armature_get_visible(Object *ob, ViewLayer *view_layer, View3D *v3d) +{ + Object *ob_armature = BKE_object_pose_armature_get(ob); + if (ob_armature) { + Base *base = BKE_view_layer_base_find(view_layer, ob_armature); + if (base) { + if (BASE_VISIBLE(v3d, base)) { + return ob_armature; + } + } + } + return nullptr; +} + +/** + * Access pose array with special check to get pose object when in weight paint mode. + */ +Object **BKE_object_pose_array_get_ex(ViewLayer *view_layer, + View3D *v3d, + uint *r_objects_len, + bool unique) +{ + Object *ob_active = OBACT(view_layer); + Object *ob_pose = BKE_object_pose_armature_get(ob_active); + Object **objects = nullptr; + if (ob_pose == ob_active) { + ObjectsInModeParams ob_params = {0}; + ob_params.object_mode = OB_MODE_POSE; + ob_params.no_dup_data = unique; + + objects = BKE_view_layer_array_from_objects_in_mode_params( + view_layer, v3d, r_objects_len, &ob_params); + } + else if (ob_pose != nullptr) { + *r_objects_len = 1; + objects = (Object **)MEM_mallocN(sizeof(*objects), __func__); + objects[0] = ob_pose; + } + else { + *r_objects_len = 0; + objects = (Object **)MEM_mallocN(0, __func__); + } + return objects; +} +Object **BKE_object_pose_array_get_unique(ViewLayer *view_layer, View3D *v3d, uint *r_objects_len) +{ + return BKE_object_pose_array_get_ex(view_layer, v3d, r_objects_len, true); +} +Object **BKE_object_pose_array_get(ViewLayer *view_layer, View3D *v3d, uint *r_objects_len) +{ + return BKE_object_pose_array_get_ex(view_layer, v3d, r_objects_len, false); +} + +Base **BKE_object_pose_base_array_get_ex(ViewLayer *view_layer, + View3D *v3d, + uint *r_bases_len, + bool unique) +{ + Base *base_active = BASACT(view_layer); + Object *ob_pose = base_active ? BKE_object_pose_armature_get(base_active->object) : nullptr; + Base *base_pose = nullptr; + Base **bases = nullptr; + + if (base_active) { + if (ob_pose == base_active->object) { + base_pose = base_active; + } + else { + base_pose = BKE_view_layer_base_find(view_layer, ob_pose); + } + } + + if (base_active && (base_pose == base_active)) { + ObjectsInModeParams ob_params = {0}; + ob_params.object_mode = OB_MODE_POSE; + ob_params.no_dup_data = unique; + + bases = BKE_view_layer_array_from_bases_in_mode_params( + view_layer, v3d, r_bases_len, &ob_params); + } + else if (base_pose != nullptr) { + *r_bases_len = 1; + bases = (Base **)MEM_mallocN(sizeof(*bases), __func__); + bases[0] = base_pose; + } + else { + *r_bases_len = 0; + bases = (Base **)MEM_mallocN(0, __func__); + } + return bases; +} +Base **BKE_object_pose_base_array_get_unique(ViewLayer *view_layer, View3D *v3d, uint *r_bases_len) +{ + return BKE_object_pose_base_array_get_ex(view_layer, v3d, r_bases_len, true); +} +Base **BKE_object_pose_base_array_get(ViewLayer *view_layer, View3D *v3d, uint *r_bases_len) +{ + return BKE_object_pose_base_array_get_ex(view_layer, v3d, r_bases_len, false); +} + +void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src) +{ + copy_v3_v3(ob_tar->loc, ob_src->loc); + copy_v3_v3(ob_tar->rot, ob_src->rot); + copy_v4_v4(ob_tar->quat, ob_src->quat); + copy_v3_v3(ob_tar->rotAxis, ob_src->rotAxis); + ob_tar->rotAngle = ob_src->rotAngle; + ob_tar->rotmode = ob_src->rotmode; + copy_v3_v3(ob_tar->scale, ob_src->scale); +} + +/** + * Perform deep-copy of object and its 'children' data-blocks (obdata, materials, actions, etc.). + * + * \param dupflag: Controls which sub-data are also duplicated + * (see #eDupli_ID_Flags in DNA_userdef_types.h). + * + * \note This function does not do any remapping to new IDs, caller must do it + * (\a #BKE_libblock_relink_to_newid()). + * \note Caller MUST free \a newid pointers itself (#BKE_main_id_newptr_and_tag_clear()) and call + * updates of DEG too (#DAG_relations_tag_update()). + */ +Object *BKE_object_duplicate(Main *bmain, Object *ob, uint dupflag, uint duplicate_options) +{ + const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; + const bool is_root_id = (duplicate_options & LIB_ID_DUPLICATE_IS_ROOT_ID) != 0; + int copy_flags = LIB_ID_COPY_DEFAULT; + + if (!is_subprocess) { + BKE_main_id_newptr_and_tag_clear(bmain); + } + else { + /* In case copying object is a sub-process of collection (or scene) copying, do not try to + * re-assign RB objects to existing RBW collections. */ + copy_flags |= LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING; + } + if (is_root_id) { + /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate + * all expected linked data. */ + if (ID_IS_LINKED(ob)) { + dupflag |= USER_DUP_LINKED_ID; + } + duplicate_options &= ~LIB_ID_DUPLICATE_IS_ROOT_ID; + } + + Material ***matarar; + + Object *obn = (Object *)BKE_id_copy_for_duplicate(bmain, &ob->id, dupflag, copy_flags); + + /* 0 == full linked. */ + if (dupflag == 0) { + return obn; + } + + if (dupflag & USER_DUP_MAT) { + for (int i = 0; i < obn->totcol; i++) { + BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag, copy_flags); + } + } + if (dupflag & USER_DUP_PSYS) { + LISTBASE_FOREACH (ParticleSystem *, psys, &obn->particlesystem) { + BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag, copy_flags); + } + } + + ID *id_old = (ID *)obn->data; + ID *id_new = nullptr; + const bool need_to_duplicate_obdata = (id_old != nullptr) && (id_old->newid == nullptr); + + switch (obn->type) { + case OB_MESH: + if (dupflag & USER_DUP_MESH) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_CURVE: + if (dupflag & USER_DUP_CURVE) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_SURF: + if (dupflag & USER_DUP_SURF) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_FONT: + if (dupflag & USER_DUP_FONT) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_MBALL: + if (dupflag & USER_DUP_MBALL) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_LAMP: + if (dupflag & USER_DUP_LAMP) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_ARMATURE: + if (dupflag & USER_DUP_ARM) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_LATTICE: + if (dupflag & USER_DUP_LATTICE) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_CAMERA: + if (dupflag & USER_DUP_CAMERA) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_LIGHTPROBE: + if (dupflag & USER_DUP_LIGHTPROBE) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_SPEAKER: + if (dupflag & USER_DUP_SPEAKER) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_GPENCIL: + if (dupflag & USER_DUP_GPENCIL) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_HAIR: + if (dupflag & USER_DUP_HAIR) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_POINTCLOUD: + if (dupflag & USER_DUP_POINTCLOUD) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + case OB_VOLUME: + if (dupflag & USER_DUP_VOLUME) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); + } + break; + } + + /* If obdata has been copied, we may also have to duplicate the materials assigned to it. */ + if (need_to_duplicate_obdata && !ELEM(id_new, nullptr, id_old)) { + if (dupflag & USER_DUP_MAT) { + matarar = BKE_object_material_array_p(obn); + if (matarar) { + for (int i = 0; i < obn->totcol; i++) { + BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], dupflag, copy_flags); + } + } + } + } + + if (!is_subprocess) { + /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ + BKE_libblock_relink_to_newid((ID *)&obn->id); + +#ifndef NDEBUG + /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0); + } + FOREACH_MAIN_ID_END; +#endif + + /* Cleanup. */ + BKE_main_id_newptr_and_tag_clear(bmain); + } + + if (obn->type == OB_ARMATURE) { + DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY); + if (obn->pose) { + BKE_pose_tag_recalc(bmain, obn->pose); + } + // BKE_pose_rebuild(bmain, obn, obn->data, true); + } + + if (obn->data != nullptr) { + DEG_id_tag_update_ex(bmain, (ID *)obn->data, ID_RECALC_EDITORS); + } + + return obn; +} + +/** + * Returns true if the Object is from an external blend file (libdata). + */ +bool BKE_object_is_libdata(const Object *ob) +{ + return (ob && ID_IS_LINKED(ob)); +} + +/** + * Returns true if the Object data is from an external blend file (libdata). + */ +bool BKE_object_obdata_is_libdata(const Object *ob) +{ + /* Linked objects with local obdata are forbidden! */ + BLI_assert(!ob || !ob->data || (ID_IS_LINKED(ob) ? ID_IS_LINKED(ob->data) : true)); + return (ob && ob->data && ID_IS_LINKED(ob->data)); +} + +/* -------------------------------------------------------------------- */ +/** \name Object Proxy API + * \{ */ + +/* when you make proxy, ensure the exposed layers are extern */ +static void armature_set_id_extern(Object *ob) +{ + bArmature *arm = (bArmature *)ob->data; + unsigned int lay = arm->layer_protected; + + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + if (!(pchan->bone->layer & lay)) { + id_lib_extern((ID *)pchan->custom); + } + } +} + +void BKE_object_copy_proxy_drivers(Object *ob, Object *target) +{ + if ((target->adt) && (target->adt->drivers.first)) { + + /* add new animdata block */ + if (!ob->adt) { + ob->adt = BKE_animdata_ensure_id(&ob->id); + } + + /* make a copy of all the drivers (for now), then correct any links that need fixing */ + BKE_fcurves_free(&ob->adt->drivers); + BKE_fcurves_copy(&ob->adt->drivers, &target->adt->drivers); + + LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->drivers) { + ChannelDriver *driver = fcu->driver; + + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + /* all drivers */ + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + if (dtar->id) { + if ((Object *)dtar->id == target) { + dtar->id = (ID *)ob; + } + else { + /* only on local objects because this causes indirect links + * 'a -> b -> c', blend to point directly to a.blend + * when a.blend has a proxy that's linked into `c.blend`. */ + if (!ID_IS_LINKED(ob)) { + id_lib_extern((ID *)dtar->id); + } + } + } + } + DRIVER_TARGETS_LOOPER_END; + } + } + } +} + +/** + * Proxy rule: + * - lib_object->proxy_from == the one we borrow from, set temporally while object_update. + * - local_object->proxy == pointer to library object, saved in files and read. + * - local_object->proxy_group == pointer to collection dupli-object, saved in files and read. + */ +void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob) +{ + /* paranoia checks */ + if (ID_IS_LINKED(ob) || !ID_IS_LINKED(target)) { + CLOG_ERROR(&LOG, "cannot make proxy"); + return; + } + + ob->proxy = target; + id_us_plus(&target->id); + ob->proxy_group = cob; + + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + DEG_id_tag_update(&target->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + + /* copy transform + * - cob means this proxy comes from a collection, just apply the matrix + * so the object won't move from its dupli-transform. + * + * - no cob means this is being made from a linked object, + * this is closer to making a copy of the object - in-place. */ + if (cob) { + ob->rotmode = target->rotmode; + mul_m4_m4m4(ob->obmat, cob->obmat, target->obmat); + if (cob->instance_collection) { /* should always be true */ + float tvec[3]; + mul_v3_mat3_m4v3(tvec, ob->obmat, cob->instance_collection->instance_offset); + sub_v3_v3(ob->obmat[3], tvec); + } + BKE_object_apply_mat4(ob, ob->obmat, false, true); + } + else { + BKE_object_transform_copy(ob, target); + ob->parent = target->parent; /* libdata */ + copy_m4_m4(ob->parentinv, target->parentinv); + } + + /* copy animdata stuff - drivers only for now... */ + BKE_object_copy_proxy_drivers(ob, target); + + /* skip constraints? */ + /* FIXME: this is considered by many as a bug */ + + /* set object type and link to data */ + ob->type = target->type; + ob->data = target->data; + id_us_plus((ID *)ob->data); /* ensures lib data becomes LIB_TAG_EXTERN */ + + /* copy material and index information */ + ob->actcol = ob->totcol = 0; + if (ob->mat) { + MEM_freeN(ob->mat); + } + if (ob->matbits) { + MEM_freeN(ob->matbits); + } + ob->mat = nullptr; + ob->matbits = nullptr; + if ((target->totcol) && (target->mat) && OB_TYPE_SUPPORT_MATERIAL(ob->type)) { + int i; + + ob->actcol = target->actcol; + ob->totcol = target->totcol; + + ob->mat = (Material **)MEM_dupallocN(target->mat); + ob->matbits = (char *)MEM_dupallocN(target->matbits); + for (i = 0; i < target->totcol; i++) { + /* don't need to run BKE_object_materials_test + * since we know this object is new and not used elsewhere */ + id_us_plus((ID *)ob->mat[i]); + } + } + + /* type conversions */ + if (target->type == OB_ARMATURE) { + copy_object_pose(ob, target, 0); /* data copy, object pointers in constraints */ + BKE_pose_rest(ob->pose, false); /* clear all transforms in channels */ + BKE_pose_rebuild(bmain, ob, (bArmature *)ob->data, true); /* set all internal links */ + + armature_set_id_extern(ob); + } + else if (target->type == OB_EMPTY) { + ob->empty_drawtype = target->empty_drawtype; + ob->empty_drawsize = target->empty_drawsize; + } + + /* copy IDProperties */ + if (ob->id.properties) { + IDP_FreeProperty(ob->id.properties); + ob->id.properties = nullptr; + } + if (target->id.properties) { + ob->id.properties = IDP_CopyProperty(target->id.properties); + } + + /* copy drawtype info */ + ob->dt = target->dt; +} + +/** + * Use with newly created objects to set their size + * (used to apply scene-scale). + */ +void BKE_object_obdata_size_init(struct Object *ob, const float size) +{ + /* apply radius as a scale to types that support it */ + switch (ob->type) { + case OB_EMPTY: { + ob->empty_drawsize *= size; + break; + } + case OB_FONT: { + Curve *cu = (Curve *)ob->data; + cu->fsize *= size; + break; + } + case OB_CAMERA: { + Camera *cam = (Camera *)ob->data; + cam->drawsize *= size; + break; + } + case OB_LAMP: { + Light *lamp = (Light *)ob->data; + lamp->dist *= size; + lamp->area_size *= size; + lamp->area_sizey *= size; + lamp->area_sizez *= size; + break; + } + /* Only lattice (not mesh, curve, mball...), + * because its got data when newly added */ + case OB_LATTICE: { + Lattice *lt = (Lattice *)ob->data; + float mat[4][4]; + + unit_m4(mat); + scale_m4_fl(mat, size); + + BKE_lattice_transform(lt, (float(*)[4])mat, false); + break; + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Matrix Get/Set API + * \{ */ + +void BKE_object_scale_to_mat3(Object *ob, float mat[3][3]) +{ + float vec[3]; + mul_v3_v3v3(vec, ob->scale, ob->dscale); + size_to_mat3(mat, vec); +} + +void BKE_object_rot_to_mat3(const Object *ob, float mat[3][3], bool use_drot) +{ + float rmat[3][3], dmat[3][3]; + + /* 'dmat' is the delta-rotation matrix, which will get (pre)multiplied + * with the rotation matrix to yield the appropriate rotation + */ + + /* rotations may either be quats, eulers (with various rotation orders), or axis-angle */ + if (ob->rotmode > 0) { + /* Euler rotations + * (will cause gimbal lock, but this can be alleviated a bit with rotation orders). */ + eulO_to_mat3(rmat, ob->rot, ob->rotmode); + eulO_to_mat3(dmat, ob->drot, ob->rotmode); + } + else if (ob->rotmode == ROT_MODE_AXISANGLE) { + /* axis-angle - not really that great for 3D-changing orientations */ + axis_angle_to_mat3(rmat, ob->rotAxis, ob->rotAngle); + axis_angle_to_mat3(dmat, ob->drotAxis, ob->drotAngle); + } + else { + /* quats are normalized before use to eliminate scaling issues */ + float tquat[4]; + + normalize_qt_qt(tquat, ob->quat); + quat_to_mat3(rmat, tquat); + + normalize_qt_qt(tquat, ob->dquat); + quat_to_mat3(dmat, tquat); + } + + /* combine these rotations */ + if (use_drot) { + mul_m3_m3m3(mat, dmat, rmat); + } + else { + copy_m3_m3(mat, rmat); + } +} + +void BKE_object_mat3_to_rot(Object *ob, float mat[3][3], bool use_compat) +{ + BLI_ASSERT_UNIT_M3(mat); + + switch (ob->rotmode) { + case ROT_MODE_QUAT: { + float dquat[4]; + mat3_normalized_to_quat(ob->quat, mat); + normalize_qt_qt(dquat, ob->dquat); + invert_qt_normalized(dquat); + mul_qt_qtqt(ob->quat, dquat, ob->quat); + break; + } + case ROT_MODE_AXISANGLE: { + float quat[4]; + float dquat[4]; + + /* without drot we could apply 'mat' directly */ + mat3_normalized_to_quat(quat, mat); + axis_angle_to_quat(dquat, ob->drotAxis, ob->drotAngle); + invert_qt_normalized(dquat); + mul_qt_qtqt(quat, dquat, quat); + quat_to_axis_angle(ob->rotAxis, &ob->rotAngle, quat); + break; + } + default: /* euler */ + { + float quat[4]; + float dquat[4]; + + /* without drot we could apply 'mat' directly */ + mat3_normalized_to_quat(quat, mat); + eulO_to_quat(dquat, ob->drot, ob->rotmode); + invert_qt_normalized(dquat); + mul_qt_qtqt(quat, dquat, quat); + /* end drot correction */ + + if (use_compat) { + quat_to_compatible_eulO(ob->rot, ob->rot, ob->rotmode, quat); + } + else { + quat_to_eulO(ob->rot, ob->rotmode, quat); + } + break; + } + } +} + +void BKE_object_tfm_protected_backup(const Object *ob, ObjectTfmProtectedChannels *obtfm) +{ + +#define TFMCPY(_v) (obtfm->_v = ob->_v) +#define TFMCPY3D(_v) copy_v3_v3(obtfm->_v, ob->_v) +#define TFMCPY4D(_v) copy_v4_v4(obtfm->_v, ob->_v) + + TFMCPY3D(loc); + TFMCPY3D(dloc); + TFMCPY3D(scale); + TFMCPY3D(dscale); + TFMCPY3D(rot); + TFMCPY3D(drot); + TFMCPY4D(quat); + TFMCPY4D(dquat); + TFMCPY3D(rotAxis); + TFMCPY3D(drotAxis); + TFMCPY(rotAngle); + TFMCPY(drotAngle); + +#undef TFMCPY +#undef TFMCPY3D +#undef TFMCPY4D +} + +void BKE_object_tfm_protected_restore(Object *ob, + const ObjectTfmProtectedChannels *obtfm, + const short protectflag) +{ + unsigned int i; + + for (i = 0; i < 3; i++) { + if (protectflag & (OB_LOCK_LOCX << i)) { + ob->loc[i] = obtfm->loc[i]; + ob->dloc[i] = obtfm->dloc[i]; + } + + if (protectflag & (OB_LOCK_SCALEX << i)) { + ob->scale[i] = obtfm->scale[i]; + ob->dscale[i] = obtfm->dscale[i]; + } + + if (protectflag & (OB_LOCK_ROTX << i)) { + ob->rot[i] = obtfm->rot[i]; + ob->drot[i] = obtfm->drot[i]; + + ob->quat[i + 1] = obtfm->quat[i + 1]; + ob->dquat[i + 1] = obtfm->dquat[i + 1]; + + ob->rotAxis[i] = obtfm->rotAxis[i]; + ob->drotAxis[i] = obtfm->drotAxis[i]; + } + } + + if ((protectflag & OB_LOCK_ROT4D) && (protectflag & OB_LOCK_ROTW)) { + ob->quat[0] = obtfm->quat[0]; + ob->dquat[0] = obtfm->dquat[0]; + + ob->rotAngle = obtfm->rotAngle; + ob->drotAngle = obtfm->drotAngle; + } +} + +void BKE_object_tfm_copy(Object *object_dst, const Object *object_src) +{ +#define TFMCPY(_v) (object_dst->_v = object_src->_v) +#define TFMCPY3D(_v) copy_v3_v3(object_dst->_v, object_src->_v) +#define TFMCPY4D(_v) copy_v4_v4(object_dst->_v, object_src->_v) + + TFMCPY3D(loc); + TFMCPY3D(dloc); + TFMCPY3D(scale); + TFMCPY3D(dscale); + TFMCPY3D(rot); + TFMCPY3D(drot); + TFMCPY4D(quat); + TFMCPY4D(dquat); + TFMCPY3D(rotAxis); + TFMCPY3D(drotAxis); + TFMCPY(rotAngle); + TFMCPY(drotAngle); + +#undef TFMCPY +#undef TFMCPY3D +#undef TFMCPY4D +} + +void BKE_object_to_mat3(Object *ob, float r_mat[3][3]) /* no parent */ +{ + float smat[3][3]; + float rmat[3][3]; + + /* Scale. */ + BKE_object_scale_to_mat3(ob, smat); + + /* Rotation. */ + BKE_object_rot_to_mat3(ob, rmat, true); + mul_m3_m3m3(r_mat, rmat, smat); +} + +void BKE_object_to_mat4(Object *ob, float r_mat[4][4]) +{ + float tmat[3][3]; + + BKE_object_to_mat3(ob, tmat); + + copy_m4_m3(r_mat, tmat); + + add_v3_v3v3(r_mat[3], ob->loc, ob->dloc); +} + +void BKE_object_matrix_local_get(struct Object *ob, float r_mat[4][4]) +{ + if (ob->parent) { + float par_imat[4][4]; + + BKE_object_get_parent_matrix(ob, ob->parent, par_imat); + invert_m4(par_imat); + mul_m4_m4m4(r_mat, par_imat, ob->obmat); + } + else { + copy_m4_m4(r_mat, ob->obmat); + } +} + +/** + * \return success if \a mat is set. + */ +static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4]) +{ + Curve *cu = (Curve *)par->data; + float vec[4], dir[3], quat[4], radius, ctime; + + /* NOTE: Curve cache is supposed to be evaluated here already, however there + * are cases where we can not guarantee that. This includes, for example, + * dependency cycles. We can't correct anything from here, since that would + * cause a threading conflicts. + * + * TODO(sergey): Some of the legit looking cases like T56619 need to be + * looked into, and maybe curve cache (and other dependencies) are to be + * evaluated prior to conversion. */ + if (par->runtime.curve_cache == nullptr) { + return false; + } + if (par->runtime.curve_cache->anim_path_accum_length == nullptr) { + return false; + } + + /* ctime is now a proper var setting of Curve which gets set by Animato like any other var + * that's animated, but this will only work if it actually is animated. + * + * We divide the curve-time calculated in the previous step by the length of the path, + * to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. + */ + if (cu->pathlen) { + ctime = cu->ctime / cu->pathlen; + } + else { + ctime = cu->ctime; + } + + if (cu->flag & CU_PATH_CLAMP) { + CLAMP(ctime, 0.0f, 1.0f); + } + + unit_m4(r_mat); + + /* vec: 4 items! */ + if (BKE_where_on_path( + par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : nullptr, &radius, nullptr)) { + if (cu->flag & CU_FOLLOW) { + quat_apply_track(quat, ob->trackflag, ob->upflag); + normalize_qt(quat); + quat_to_mat4(r_mat, quat); + } + if (cu->flag & CU_PATH_RADIUS) { + float tmat[4][4], rmat[4][4]; + scale_m4_fl(tmat, radius); + mul_m4_m4m4(rmat, tmat, r_mat); + copy_m4_m4(r_mat, rmat); + } + copy_v3_v3(r_mat[3], vec); + } + + return true; +} + +static void ob_parbone(Object *ob, Object *par, float r_mat[4][4]) +{ + float vec[3]; + + if (par->type != OB_ARMATURE) { + unit_m4(r_mat); + return; + } + + /* Make sure the bone is still valid */ + bPoseChannel *pchan = BKE_pose_channel_find_name(par->pose, ob->parsubstr); + if (!pchan || !pchan->bone) { + CLOG_WARN( + &LOG, "Parent Bone: '%s' for Object: '%s' doesn't exist", ob->parsubstr, ob->id.name + 2); + unit_m4(r_mat); + return; + } + + /* get bone transform */ + if (pchan->bone->flag & BONE_RELATIVE_PARENTING) { + /* the new option uses the root - expected behavior, but differs from old... */ + /* XXX check on version patching? */ + copy_m4_m4(r_mat, pchan->chan_mat); + } + else { + copy_m4_m4(r_mat, pchan->pose_mat); + + /* but for backwards compatibility, the child has to move to the tail */ + copy_v3_v3(vec, r_mat[1]); + mul_v3_fl(vec, pchan->bone->length); + add_v3_v3(r_mat[3], vec); + } +} + +static void give_parvert(Object *par, int nr, float vec[3]) +{ + zero_v3(vec); + + if (par->type == OB_MESH) { + Mesh *me = (Mesh *)par->data; + BMEditMesh *em = me->edit_mesh; + Mesh *me_eval = (em) ? em->mesh_eval_final : BKE_object_get_evaluated_mesh(par); + + if (me_eval) { + int count = 0; + int numVerts = me_eval->totvert; + + if (em && me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) { + numVerts = em->bm->totvert; + if (em->bm->elem_table_dirty & BM_VERT) { +#ifdef VPARENT_THREADING_HACK + BLI_mutex_lock(&vparent_lock); + if (em->bm->elem_table_dirty & BM_VERT) { + BM_mesh_elem_table_ensure(em->bm, BM_VERT); + } + BLI_mutex_unlock(&vparent_lock); +#else + BLI_assert_msg(0, "Not safe for threading"); + BM_mesh_elem_table_ensure(em->bm, BM_VERT); +#endif + } + if (nr < numVerts) { + if (me_eval && me_eval->runtime.edit_data && me_eval->runtime.edit_data->vertexCos) { + add_v3_v3(vec, me_eval->runtime.edit_data->vertexCos[nr]); + } + else { + const BMVert *v = BM_vert_at_index(em->bm, nr); + add_v3_v3(vec, v->co); + } + count++; + } + } + else if (CustomData_has_layer(&me_eval->vdata, CD_ORIGINDEX)) { + const int *index = (const int *)CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); + /* Get the average of all verts with (original index == nr). */ + for (int i = 0; i < numVerts; i++) { + if (index[i] == nr) { + add_v3_v3(vec, me_eval->mvert[i].co); + count++; + } + } + } + else { + if (nr < numVerts) { + add_v3_v3(vec, me_eval->mvert[nr].co); + count++; + } + } + + if (count == 0) { + /* keep as 0, 0, 0 */ + } + else if (count > 0) { + mul_v3_fl(vec, 1.0f / count); + } + else { + /* use first index if its out of range */ + if (me_eval->totvert) { + copy_v3_v3(vec, me_eval->mvert[0].co); + } + } + } + else { + CLOG_ERROR(&LOG, + "Evaluated mesh is needed to solve parenting, " + "object position can be wrong now"); + } + } + else if (ELEM(par->type, OB_CURVE, OB_SURF)) { + ListBase *nurb; + + /* Unless there's some weird depsgraph failure the cache should exist. */ + BLI_assert(par->runtime.curve_cache != nullptr); + + if (par->runtime.curve_cache->deformed_nurbs.first != nullptr) { + nurb = &par->runtime.curve_cache->deformed_nurbs; + } + else { + Curve *cu = (Curve *)par->data; + nurb = BKE_curve_nurbs_get(cu); + } + + BKE_nurbList_index_get_co(nurb, nr, vec); + } + else if (par->type == OB_LATTICE) { + Lattice *latt = (Lattice *)par->data; + DispList *dl = par->runtime.curve_cache ? + BKE_displist_find(&par->runtime.curve_cache->disp, DL_VERTS) : + nullptr; + float(*co)[3] = dl ? (float(*)[3])dl->verts : nullptr; + int tot; + + if (latt->editlatt) { + latt = latt->editlatt->latt; + } + + tot = latt->pntsu * latt->pntsv * latt->pntsw; + + /* ensure dl is correct size */ + BLI_assert(dl == nullptr || dl->nr == tot); + + if (nr < tot) { + if (co) { + copy_v3_v3(vec, co[nr]); + } + else { + copy_v3_v3(vec, latt->def[nr].vec); + } + } + } +} + +static void ob_parvert3(Object *ob, Object *par, float r_mat[4][4]) +{ + /* in local ob space */ + if (OB_TYPE_SUPPORT_PARVERT(par->type)) { + float cmat[3][3], v1[3], v2[3], v3[3], q[4]; + + give_parvert(par, ob->par1, v1); + give_parvert(par, ob->par2, v2); + give_parvert(par, ob->par3, v3); + + tri_to_quat(q, v1, v2, v3); + quat_to_mat3(cmat, q); + copy_m4_m3(r_mat, cmat); + + mid_v3_v3v3v3(r_mat[3], v1, v2, v3); + } + else { + unit_m4(r_mat); + } +} + +void BKE_object_get_parent_matrix(Object *ob, Object *par, float r_parentmat[4][4]) +{ + float tmat[4][4]; + float vec[3]; + + switch (ob->partype & PARTYPE) { + case PAROBJECT: { + bool ok = false; + if (par->type == OB_CURVE) { + if ((((Curve *)par->data)->flag & CU_PATH) && (ob_parcurve(ob, par, tmat))) { + ok = true; + } + } + + if (ok) { + mul_m4_m4m4(r_parentmat, par->obmat, tmat); + } + else { + copy_m4_m4(r_parentmat, par->obmat); + } + + break; + } + case PARBONE: + ob_parbone(ob, par, tmat); + mul_m4_m4m4(r_parentmat, par->obmat, tmat); + break; + + case PARVERT1: + unit_m4(r_parentmat); + give_parvert(par, ob->par1, vec); + mul_v3_m4v3(r_parentmat[3], par->obmat, vec); + break; + case PARVERT3: + ob_parvert3(ob, par, tmat); + + mul_m4_m4m4(r_parentmat, par->obmat, tmat); + break; + + case PARSKEL: + copy_m4_m4(r_parentmat, par->obmat); + break; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Matrix Evaluation API + * \{ */ + +/** + * \param r_originmat: Optional matrix that stores the space the object is in + * (without its own matrix applied) + */ +static void solve_parenting( + Object *ob, Object *par, const bool set_origin, float r_obmat[4][4], float r_originmat[3][3]) +{ + float totmat[4][4]; + float tmat[4][4]; + float locmat[4][4]; + + BKE_object_to_mat4(ob, locmat); + + BKE_object_get_parent_matrix(ob, par, totmat); + + /* total */ + mul_m4_m4m4(tmat, totmat, ob->parentinv); + mul_m4_m4m4(r_obmat, tmat, locmat); + + if (r_originmat) { + /* usable originmat */ + copy_m3_m4(r_originmat, tmat); + } + + /* origin, for help line */ + if (set_origin) { + if ((ob->partype & PARTYPE) == PARSKEL) { + copy_v3_v3(ob->runtime.parent_display_origin, par->obmat[3]); + } + else { + copy_v3_v3(ob->runtime.parent_display_origin, totmat[3]); + } + } +} + +static void object_where_is_calc_ex(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + float ctime, + RigidBodyWorld *rbw, + float r_originmat[3][3]) +{ + if (ob->parent) { + Object *par = ob->parent; + + /* calculate parent matrix */ + solve_parenting(ob, par, true, ob->obmat, r_originmat); + } + else { + BKE_object_to_mat4(ob, ob->obmat); + } + + /* try to fall back to the scene rigid body world if none given */ + rbw = rbw ? rbw : scene->rigidbody_world; + /* read values pushed into RBO from sim/cache... */ + BKE_rigidbody_sync_transforms(rbw, ob, ctime); + + /* solve constraints */ + if (ob->constraints.first && !(ob->transflag & OB_NO_CONSTRAINTS)) { + bConstraintOb *cob; + cob = BKE_constraints_make_evalob(depsgraph, scene, ob, nullptr, CONSTRAINT_OBTYPE_OBJECT); + BKE_constraints_solve(depsgraph, &ob->constraints, cob, ctime); + BKE_constraints_clear_evalob(cob); + } + + /* set negative scale flag in object */ + if (is_negative_m4(ob->obmat)) { + ob->transflag |= OB_NEG_SCALE; + } + else { + ob->transflag &= ~OB_NEG_SCALE; + } +} + +void BKE_object_where_is_calc_time(Depsgraph *depsgraph, Scene *scene, Object *ob, float ctime) +{ + /* Execute drivers and animation. */ + const bool flush_to_original = DEG_is_active(depsgraph); + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, + ctime); + BKE_animsys_evaluate_animdata( + &ob->id, ob->adt, &anim_eval_context, ADT_RECALC_ALL, flush_to_original); + object_where_is_calc_ex(depsgraph, scene, ob, ctime, nullptr, nullptr); +} + +/** + * Calculate object transformation matrix without recalculating dependencies and + * constraints -- assume dependencies are already solved by depsgraph. + * No changes to object and its parent would be done. + * Used for bundles orientation in 3d space relative to parented blender camera. + */ +void BKE_object_where_is_calc_mat4(Object *ob, float r_obmat[4][4]) +{ + if (ob->parent) { + Object *par = ob->parent; + solve_parenting(ob, par, false, r_obmat, nullptr); + } + else { + BKE_object_to_mat4(ob, r_obmat); + } +} + +void BKE_object_where_is_calc_ex( + Depsgraph *depsgraph, Scene *scene, RigidBodyWorld *rbw, Object *ob, float r_originmat[3][3]) +{ + float ctime = DEG_get_ctime(depsgraph); + object_where_is_calc_ex(depsgraph, scene, ob, ctime, rbw, r_originmat); +} +void BKE_object_where_is_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) +{ + float ctime = DEG_get_ctime(depsgraph); + object_where_is_calc_ex(depsgraph, scene, ob, ctime, nullptr, nullptr); +} + +/** + * For calculation of the inverse parent transform, only used for editor. + * + * It assumes the object parent is already in the depsgraph. + * Otherwise, after changing ob->parent you need to call: + * - #DEG_relations_tag_update(bmain); + * - #BKE_scene_graph_update_tagged(depsgraph, bmain); + */ +void BKE_object_workob_calc_parent(Depsgraph *depsgraph, Scene *scene, Object *ob, Object *workob) +{ + BKE_object_workob_clear(workob); + + unit_m4(workob->obmat); + unit_m4(workob->parentinv); + unit_m4(workob->constinv); + + /* Since this is used while calculating parenting, + * at this moment ob_eval->parent is still nullptr. */ + workob->parent = DEG_get_evaluated_object(depsgraph, ob->parent); + + workob->trackflag = ob->trackflag; + workob->upflag = ob->upflag; + + workob->partype = ob->partype; + workob->par1 = ob->par1; + workob->par2 = ob->par2; + workob->par3 = ob->par3; + + /* The effects of constraints should NOT be included in the parent-inverse matrix. Constraints + * are supposed to be applied after the object's local loc/rot/scale. If the (inverted) effect of + * constraints would be included in the parent inverse matrix, these would be applied before the + * object's local loc/rot/scale instead of after. For example, a "Copy Rotation" constraint would + * rotate the object's local translation as well. See T82156. */ + + BLI_strncpy(workob->parsubstr, ob->parsubstr, sizeof(workob->parsubstr)); + + BKE_object_where_is_calc(depsgraph, scene, workob); +} + +/** + * Applies the global transformation \a mat to the \a ob using a relative parent space if + * supplied. + * + * \param mat: the global transformation mat that the object should be set object to. + * \param parent: the parent space in which this object will be set relative to + * (should probably always be parent_eval). + * \param use_compat: true to ensure that rotations are set using the + * min difference between the old and new orientation. + */ +void BKE_object_apply_mat4_ex(Object *ob, + const float mat[4][4], + Object *parent, + const float parentinv[4][4], + const bool use_compat) +{ + /* see BKE_pchan_apply_mat4() for the equivalent 'pchan' function */ + + float rot[3][3]; + + if (parent != nullptr) { + float rmat[4][4], diff_mat[4][4], imat[4][4], parent_mat[4][4]; + + BKE_object_get_parent_matrix(ob, parent, parent_mat); + + mul_m4_m4m4(diff_mat, parent_mat, parentinv); + invert_m4_m4(imat, diff_mat); + mul_m4_m4m4(rmat, imat, mat); /* get the parent relative matrix */ + + /* same as below, use rmat rather than mat */ + mat4_to_loc_rot_size(ob->loc, rot, ob->scale, rmat); + } + else { + mat4_to_loc_rot_size(ob->loc, rot, ob->scale, mat); + } + + BKE_object_mat3_to_rot(ob, rot, use_compat); + + sub_v3_v3(ob->loc, ob->dloc); + + if (ob->dscale[0] != 0.0f) { + ob->scale[0] /= ob->dscale[0]; + } + if (ob->dscale[1] != 0.0f) { + ob->scale[1] /= ob->dscale[1]; + } + if (ob->dscale[2] != 0.0f) { + ob->scale[2] /= ob->dscale[2]; + } + + /* BKE_object_mat3_to_rot handles delta rotations */ +} + +/** + * XXX: should be removed after COW operators port to use BKE_object_apply_mat4_ex directly. + */ +void BKE_object_apply_mat4(Object *ob, + const float mat[4][4], + const bool use_compat, + const bool use_parent) +{ + BKE_object_apply_mat4_ex(ob, mat, use_parent ? ob->parent : nullptr, ob->parentinv, use_compat); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Bounding Box API + * \{ */ + +BoundBox *BKE_boundbox_alloc_unit(void) +{ + const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; + + BoundBox *bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "OB-BoundBox"); + BKE_boundbox_init_from_minmax(bb, min, max); + + return bb; +} + +void BKE_boundbox_init_from_minmax(BoundBox *bb, const float min[3], const float max[3]) +{ + bb->vec[0][0] = bb->vec[1][0] = bb->vec[2][0] = bb->vec[3][0] = min[0]; + bb->vec[4][0] = bb->vec[5][0] = bb->vec[6][0] = bb->vec[7][0] = max[0]; + + bb->vec[0][1] = bb->vec[1][1] = bb->vec[4][1] = bb->vec[5][1] = min[1]; + bb->vec[2][1] = bb->vec[3][1] = bb->vec[6][1] = bb->vec[7][1] = max[1]; + + bb->vec[0][2] = bb->vec[3][2] = bb->vec[4][2] = bb->vec[7][2] = min[2]; + bb->vec[1][2] = bb->vec[2][2] = bb->vec[5][2] = bb->vec[6][2] = max[2]; +} + +void BKE_boundbox_calc_center_aabb(const BoundBox *bb, float r_cent[3]) +{ + r_cent[0] = 0.5f * (bb->vec[0][0] + bb->vec[4][0]); + r_cent[1] = 0.5f * (bb->vec[0][1] + bb->vec[2][1]); + r_cent[2] = 0.5f * (bb->vec[0][2] + bb->vec[1][2]); +} + +void BKE_boundbox_calc_size_aabb(const BoundBox *bb, float r_size[3]) +{ + r_size[0] = 0.5f * fabsf(bb->vec[0][0] - bb->vec[4][0]); + r_size[1] = 0.5f * fabsf(bb->vec[0][1] - bb->vec[2][1]); + r_size[2] = 0.5f * fabsf(bb->vec[0][2] - bb->vec[1][2]); +} + +void BKE_boundbox_minmax(const BoundBox *bb, + const float obmat[4][4], + float r_min[3], + float r_max[3]) +{ + int i; + for (i = 0; i < 8; i++) { + float vec[3]; + mul_v3_m4v3(vec, obmat, bb->vec[i]); + minmax_v3v3_v3(r_min, r_max, vec); + } +} + +BoundBox *BKE_object_boundbox_get(Object *ob) +{ + BoundBox *bb = nullptr; + + switch (ob->type) { + case OB_MESH: + bb = BKE_mesh_boundbox_get(ob); + break; + case OB_CURVE: + case OB_SURF: + case OB_FONT: + bb = BKE_curve_boundbox_get(ob); + break; + case OB_MBALL: + bb = BKE_mball_boundbox_get(ob); + break; + case OB_LATTICE: + bb = BKE_lattice_boundbox_get(ob); + break; + case OB_ARMATURE: + bb = BKE_armature_boundbox_get(ob); + break; + case OB_GPENCIL: + bb = BKE_gpencil_boundbox_get(ob); + break; + case OB_HAIR: + bb = BKE_hair_boundbox_get(ob); + break; + case OB_POINTCLOUD: + bb = BKE_pointcloud_boundbox_get(ob); + break; + case OB_VOLUME: + bb = BKE_volume_boundbox_get(ob); + break; + default: + break; + } + return bb; +} + +/** + * Use this to temporally disable/enable bound-box. + */ +void BKE_object_boundbox_flag(Object *ob, int flag, const bool set) +{ + BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + if (set) { + bb->flag |= flag; + } + else { + bb->flag &= ~flag; + } + } +} + +void BKE_object_boundbox_calc_from_mesh(Object *ob, const Mesh *me_eval) +{ + float min[3], max[3]; + + INIT_MINMAX(min, max); + + if (!BKE_mesh_wrapper_minmax(me_eval, min, max)) { + zero_v3(min); + zero_v3(max); + } + + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "DM-BoundBox"); + } + + BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); + + ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Dimension Get/Set + * + * \warning Setting dimensions is prone to feedback loops in evaluation. + * \{ */ + +void BKE_object_dimensions_get(Object *ob, float r_vec[3]) +{ + BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + float scale[3]; + + mat4_to_size(scale, ob->obmat); + + r_vec[0] = fabsf(scale[0]) * (bb->vec[4][0] - bb->vec[0][0]); + r_vec[1] = fabsf(scale[1]) * (bb->vec[2][1] - bb->vec[0][1]); + r_vec[2] = fabsf(scale[2]) * (bb->vec[1][2] - bb->vec[0][2]); + } + else { + zero_v3(r_vec); + } +} + +/** + * The original scale and object matrix can be passed in so any difference + * of the objects matrix and the final matrix can be accounted for, + * typically this caused by parenting, constraints or delta-scale. + * + * Re-using these values from the object causes a feedback loop + * when multiple values are modified at once in some situations. see: T69536. + */ +void BKE_object_dimensions_set_ex(Object *ob, + const float value[3], + int axis_mask, + const float ob_scale_orig[3], + const float ob_obmat_orig[4][4]) +{ + BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + float len[3]; + + len[0] = bb->vec[4][0] - bb->vec[0][0]; + len[1] = bb->vec[2][1] - bb->vec[0][1]; + len[2] = bb->vec[1][2] - bb->vec[0][2]; + + for (int i = 0; i < 3; i++) { + if (((1 << i) & axis_mask) == 0) { + + if (ob_scale_orig != nullptr) { + const float scale_delta = len_v3(ob_obmat_orig[i]) / ob_scale_orig[i]; + if (isfinite(scale_delta)) { + len[i] *= scale_delta; + } + } + + const float scale = copysignf(value[i] / len[i], ob->scale[i]); + if (isfinite(scale)) { + ob->scale[i] = scale; + } + } + } + } +} + +void BKE_object_dimensions_set(Object *ob, const float value[3], int axis_mask) +{ + BKE_object_dimensions_set_ex(ob, value, axis_mask, nullptr, nullptr); +} + +void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool use_hidden) +{ + float vec[3]; + bool changed = false; + + switch (ob->type) { + case OB_CURVE: + case OB_FONT: + case OB_SURF: { + BoundBox bb = *BKE_curve_boundbox_get(ob); + BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); + changed = true; + break; + } + case OB_MESH: { + BoundBox bb = *BKE_mesh_boundbox_get(ob); + BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); + changed = true; + break; + } + case OB_GPENCIL: { + BoundBox bb = *BKE_gpencil_boundbox_get(ob); + BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); + changed = true; + break; + } + case OB_LATTICE: { + Lattice *lt = (Lattice *)ob->data; + BPoint *bp = lt->def; + int u, v, w; + + for (w = 0; w < lt->pntsw; w++) { + for (v = 0; v < lt->pntsv; v++) { + for (u = 0; u < lt->pntsu; u++, bp++) { + mul_v3_m4v3(vec, ob->obmat, bp->vec); + minmax_v3v3_v3(r_min, r_max, vec); + } + } + } + changed = true; + break; + } + case OB_ARMATURE: { + changed = BKE_pose_minmax(ob, r_min, r_max, use_hidden, false); + break; + } + case OB_MBALL: { + float ob_min[3], ob_max[3]; + + changed = BKE_mball_minmax_ex((const MetaBall *)ob->data, ob_min, ob_max, ob->obmat, 0); + if (changed) { + minmax_v3v3_v3(r_min, r_max, ob_min); + minmax_v3v3_v3(r_min, r_max, ob_max); + } + break; + } + case OB_HAIR: { + BoundBox bb = *BKE_hair_boundbox_get(ob); + BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); + changed = true; + break; + } + + case OB_POINTCLOUD: { + BoundBox bb = *BKE_pointcloud_boundbox_get(ob); + BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); + changed = true; + break; + } + case OB_VOLUME: { + BoundBox bb = *BKE_volume_boundbox_get(ob); + BKE_boundbox_minmax(&bb, ob->obmat, r_min, r_max); + changed = true; + break; + } + } + + if (changed == false) { + float size[3]; + + copy_v3_v3(size, ob->scale); + if (ob->type == OB_EMPTY) { + mul_v3_fl(size, ob->empty_drawsize); + } + + minmax_v3v3_v3(r_min, r_max, ob->obmat[3]); + + copy_v3_v3(vec, ob->obmat[3]); + add_v3_v3(vec, size); + minmax_v3v3_v3(r_min, r_max, vec); + + copy_v3_v3(vec, ob->obmat[3]); + sub_v3_v3(vec, size); + minmax_v3v3_v3(r_min, r_max, vec); + } +} + +void BKE_object_empty_draw_type_set(Object *ob, const int value) +{ + ob->empty_drawtype = value; + + if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { + if (!ob->iuser) { + ob->iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "image user"); + ob->iuser->flag |= IMA_ANIM_ALWAYS; + ob->iuser->frames = 100; + ob->iuser->sfra = 1; + } + } + else { + MEM_SAFE_FREE(ob->iuser); + } +} + +bool BKE_object_empty_image_frame_is_visible_in_view3d(const Object *ob, const RegionView3D *rv3d) +{ + const char visibility_flag = ob->empty_image_visibility_flag; + if (rv3d->is_persp) { + return (visibility_flag & OB_EMPTY_IMAGE_HIDE_PERSPECTIVE) == 0; + } + + return (visibility_flag & OB_EMPTY_IMAGE_HIDE_ORTHOGRAPHIC) == 0; +} + +bool BKE_object_empty_image_data_is_visible_in_view3d(const Object *ob, const RegionView3D *rv3d) +{ + /* Caller is expected to check this. */ + BLI_assert(BKE_object_empty_image_frame_is_visible_in_view3d(ob, rv3d)); + + const char visibility_flag = ob->empty_image_visibility_flag; + + if ((visibility_flag & (OB_EMPTY_IMAGE_HIDE_BACK | OB_EMPTY_IMAGE_HIDE_FRONT)) != 0) { + float eps, dot; + if (rv3d->is_persp) { + /* NOTE: we could normalize the 'view_dir' then use 'eps' + * however the issue with empty objects being visible when viewed from the side + * is only noticeable in orthographic views. */ + float view_dir[3]; + sub_v3_v3v3(view_dir, rv3d->viewinv[3], ob->obmat[3]); + dot = dot_v3v3(ob->obmat[2], view_dir); + eps = 0.0f; + } + else { + dot = dot_v3v3(ob->obmat[2], rv3d->viewinv[2]); + eps = 1e-5f; + } + if (visibility_flag & OB_EMPTY_IMAGE_HIDE_BACK) { + if (dot < eps) { + return false; + } + } + if (visibility_flag & OB_EMPTY_IMAGE_HIDE_FRONT) { + if (dot > -eps) { + return false; + } + } + } + + if (visibility_flag & OB_EMPTY_IMAGE_HIDE_NON_AXIS_ALIGNED) { + float proj[3]; + project_plane_v3_v3v3(proj, ob->obmat[2], rv3d->viewinv[2]); + const float proj_length_sq = len_squared_v3(proj); + if (proj_length_sq > 1e-5f) { + return false; + } + } + + return true; +} + +bool BKE_object_minmax_dupli(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + float r_min[3], + float r_max[3], + const bool use_hidden) +{ + bool ok = false; + if ((ob->transflag & OB_DUPLI) == 0 && ob->runtime.geometry_set_eval == nullptr) { + return ok; + } + + ListBase *lb = object_duplilist(depsgraph, scene, ob); + LISTBASE_FOREACH (DupliObject *, dob, lb) { + if ((use_hidden == false) && (dob->no_draw != 0)) { + /* pass */ + } + else { + BoundBox *bb = BKE_object_boundbox_get(dob->ob); + + if (bb) { + int i; + for (i = 0; i < 8; i++) { + float vec[3]; + mul_v3_m4v3(vec, dob->mat, bb->vec[i]); + minmax_v3v3_v3(r_min, r_max, vec); + } + + ok = true; + } + } + } + free_object_duplilist(lb); /* does restore */ + + return ok; +} + +struct GPencilStrokePointIterData { + const float (*obmat)[4]; + + void (*point_func_cb)(const float co[3], void *user_data); + void *user_data; +}; + +static void foreach_display_point_gpencil_stroke_fn(bGPDlayer *UNUSED(layer), + bGPDframe *UNUSED(frame), + bGPDstroke *stroke, + void *thunk) +{ + GPencilStrokePointIterData *iter_data = (GPencilStrokePointIterData *)thunk; + { + bGPDspoint *pt; + int i; + for (i = 0, pt = stroke->points; i < stroke->totpoints; i++, pt++) { + float co[3]; + mul_v3_m4v3(co, iter_data->obmat, &pt->x); + iter_data->point_func_cb(co, iter_data->user_data); + } + } +} + +void BKE_object_foreach_display_point(Object *ob, + const float obmat[4][4], + void (*func_cb)(const float[3], void *), + void *user_data) +{ + /* TODO: pointcloud and hair objects support */ + const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); + float co[3]; + + if (mesh_eval != nullptr) { + const MVert *mv = mesh_eval->mvert; + const int totvert = mesh_eval->totvert; + for (int i = 0; i < totvert; i++, mv++) { + mul_v3_m4v3(co, obmat, mv->co); + func_cb(co, user_data); + } + } + else if (ob->type == OB_GPENCIL) { + GPencilStrokePointIterData iter_data = {nullptr}; + iter_data.obmat = obmat; + iter_data.point_func_cb = func_cb; + iter_data.user_data = user_data; + + BKE_gpencil_visible_stroke_iter( + (bGPdata *)ob->data, nullptr, foreach_display_point_gpencil_stroke_fn, &iter_data); + } + else if (ob->runtime.curve_cache && ob->runtime.curve_cache->disp.first) { + LISTBASE_FOREACH (DispList *, dl, &ob->runtime.curve_cache->disp) { + const float *v3 = dl->verts; + int totvert = dl->nr; + int i; + + for (i = 0; i < totvert; i++, v3 += 3) { + mul_v3_m4v3(co, obmat, v3); + func_cb(co, user_data); + } + } + } +} + +void BKE_scene_foreach_display_point(Depsgraph *depsgraph, + void (*func_cb)(const float[3], void *), + void *user_data) +{ + DEG_OBJECT_ITER_BEGIN (depsgraph, + ob, + DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE | + DEG_ITER_OBJECT_FLAG_DUPLI) { + if ((ob->base_flag & BASE_SELECTED) != 0) { + BKE_object_foreach_display_point(ob, ob->obmat, func_cb, user_data); + } + } + DEG_OBJECT_ITER_END; +} + +/** + * Struct members from DNA_object_types.h + */ +struct ObTfmBack { + float loc[3], dloc[3]; + /** scale and delta scale. */ + float scale[3], dscale[3]; + /** euler rotation. */ + float rot[3], drot[3]; + /** quaternion rotation. */ + float quat[4], dquat[4]; + /** axis angle rotation - axis part. */ + float rotAxis[3], drotAxis[3]; + /** axis angle rotation - angle part. */ + float rotAngle, drotAngle; + /** final worldspace matrix with constraints & animsys applied. */ + float obmat[4][4]; + /** inverse result of parent, so that object doesn't 'stick' to parent. */ + float parentinv[4][4]; + /** inverse result of constraints. doesn't include effect of parent or object local transform. + */ + float constinv[4][4]; + /** inverse matrix of 'obmat' for during render, temporally: ipokeys of transform. */ + float imat[4][4]; +}; + +void *BKE_object_tfm_backup(Object *ob) +{ + ObTfmBack *obtfm = (ObTfmBack *)MEM_mallocN(sizeof(ObTfmBack), "ObTfmBack"); + copy_v3_v3(obtfm->loc, ob->loc); + copy_v3_v3(obtfm->dloc, ob->dloc); + copy_v3_v3(obtfm->scale, ob->scale); + copy_v3_v3(obtfm->dscale, ob->dscale); + copy_v3_v3(obtfm->rot, ob->rot); + copy_v3_v3(obtfm->drot, ob->drot); + copy_qt_qt(obtfm->quat, ob->quat); + copy_qt_qt(obtfm->dquat, ob->dquat); + copy_v3_v3(obtfm->rotAxis, ob->rotAxis); + copy_v3_v3(obtfm->drotAxis, ob->drotAxis); + obtfm->rotAngle = ob->rotAngle; + obtfm->drotAngle = ob->drotAngle; + copy_m4_m4(obtfm->obmat, ob->obmat); + copy_m4_m4(obtfm->parentinv, ob->parentinv); + copy_m4_m4(obtfm->constinv, ob->constinv); + copy_m4_m4(obtfm->imat, ob->imat); + + return (void *)obtfm; +} + +void BKE_object_tfm_restore(Object *ob, void *obtfm_pt) +{ + ObTfmBack *obtfm = (ObTfmBack *)obtfm_pt; + copy_v3_v3(ob->loc, obtfm->loc); + copy_v3_v3(ob->dloc, obtfm->dloc); + copy_v3_v3(ob->scale, obtfm->scale); + copy_v3_v3(ob->dscale, obtfm->dscale); + copy_v3_v3(ob->rot, obtfm->rot); + copy_v3_v3(ob->drot, obtfm->drot); + copy_qt_qt(ob->quat, obtfm->quat); + copy_qt_qt(ob->dquat, obtfm->dquat); + copy_v3_v3(ob->rotAxis, obtfm->rotAxis); + copy_v3_v3(ob->drotAxis, obtfm->drotAxis); + ob->rotAngle = obtfm->rotAngle; + ob->drotAngle = obtfm->drotAngle; + copy_m4_m4(ob->obmat, obtfm->obmat); + copy_m4_m4(ob->parentinv, obtfm->parentinv); + copy_m4_m4(ob->constinv, obtfm->constinv); + copy_m4_m4(ob->imat, obtfm->imat); +} + +bool BKE_object_parent_loop_check(const Object *par, const Object *ob) +{ + /* test if 'ob' is a parent somewhere in par's parents */ + if (par == nullptr) { + return false; + } + if (ob == par) { + return true; + } + return BKE_object_parent_loop_check(par->parent, ob); +} + +static void object_handle_update_proxy(Depsgraph *depsgraph, + Scene *scene, + Object *object, + const bool do_proxy_update) +{ + /* The case when this is a collection proxy, object_update is called in collection.c */ + if (object->proxy == nullptr) { + return; + } + /* set pointer in library proxy target, for copying, but restore it */ + object->proxy->proxy_from = object; + // printf("set proxy pointer for later collection stuff %s\n", ob->id.name); + + /* the no-group proxy case, we call update */ + if (object->proxy_group == nullptr) { + if (do_proxy_update) { + // printf("call update, lib ob %s proxy %s\n", ob->proxy->id.name, ob->id.name); + BKE_object_handle_update(depsgraph, scene, object->proxy); + } + } +} + +/** + * Proxy rule: + * - lib_object->proxy_from == the one we borrow from, only set temporal and cleared here. + * - local_object->proxy == pointer to library object, saved in files and read. + * + * Function below is polluted with proxy exceptions, cleanup will follow! + * + * The main object update call, for object matrix, constraints, keys and displist (modifiers) + * requires flags to be set! + * + * Ideally we shouldn't have to pass the rigid body world, + * but need bigger restructuring to avoid id. + */ +void BKE_object_handle_update_ex(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + RigidBodyWorld *rbw, + const bool do_proxy_update) +{ + const ID *object_data = (ID *)ob->data; + const bool recalc_object = (ob->id.recalc & ID_RECALC_ALL) != 0; + const bool recalc_data = (object_data != nullptr) ? + ((object_data->recalc & ID_RECALC_ALL) != 0) : + false; + if (!recalc_object && !recalc_data) { + object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update); + return; + } + /* Speed optimization for animation lookups. */ + if (ob->pose != nullptr) { + BKE_pose_channels_hash_ensure(ob->pose); + if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { + BKE_pose_update_constraint_flags(ob->pose); + } + } + if (recalc_data) { + if (ob->type == OB_ARMATURE) { + /* this happens for reading old files and to match library armatures + * with poses we do it ahead of BKE_object_where_is_calc to ensure animation + * is evaluated on the rebuilt pose, otherwise we get incorrect poses + * on file load */ + if (ob->pose == nullptr || (ob->pose->flag & POSE_RECALC)) { + /* No need to pass bmain here, we assume we do not need to rebuild DEG from here... */ + BKE_pose_rebuild(nullptr, ob, (bArmature *)ob->data, true); + } + } + } + /* XXX new animsys warning: depsgraph tag ID_RECALC_GEOMETRY should not skip drivers, + * which is only in BKE_object_where_is_calc now */ + /* XXX: should this case be ID_RECALC_TRANSFORM instead? */ + if (recalc_object || recalc_data) { + if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) { + printf("recalcob %s\n", ob->id.name + 2); + } + /* Handle proxy copy for target. */ + if (!BKE_object_eval_proxy_copy(depsgraph, ob)) { + BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, nullptr); + } + } + + if (recalc_data) { + BKE_object_handle_data_update(depsgraph, scene, ob); + } + + object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update); +} + +/** + * \warning "scene" here may not be the scene object actually resides in. + * When dealing with background-sets, "scene" is actually the active scene. + * e.g. "scene" <-- set 1 <-- set 2 ("ob" lives here) <-- set 3 <-- ... <-- set n + * rigid bodies depend on their world so use #BKE_object_handle_update_ex() + * to also pass along the current rigid body world. + */ +void BKE_object_handle_update(Depsgraph *depsgraph, Scene *scene, Object *ob) +{ + BKE_object_handle_update_ex(depsgraph, scene, ob, nullptr, true); +} + +void BKE_object_sculpt_data_create(Object *ob) +{ + BLI_assert((ob->sculpt == nullptr) && (ob->mode & OB_MODE_ALL_SCULPT)); + ob->sculpt = (SculptSession *)MEM_callocN(sizeof(SculptSession), __func__); + ob->sculpt->mode_type = (eObjectMode)ob->mode; +} + +bool BKE_object_obdata_texspace_get(Object *ob, short **r_texflag, float **r_loc, float **r_size) +{ + + if (ob->data == nullptr) { + return false; + } + + switch (GS(((ID *)ob->data)->name)) { + case ID_ME: { + BKE_mesh_texspace_get_reference((Mesh *)ob->data, r_texflag, r_loc, r_size); + break; + } + case ID_CU: { + Curve *cu = (Curve *)ob->data; + BKE_curve_texspace_ensure(cu); + if (r_texflag) { + *r_texflag = &cu->texflag; + } + if (r_loc) { + *r_loc = cu->loc; + } + if (r_size) { + *r_size = cu->size; + } + break; + } + case ID_MB: { + MetaBall *mb = (MetaBall *)ob->data; + if (r_texflag) { + *r_texflag = &mb->texflag; + } + if (r_loc) { + *r_loc = mb->loc; + } + if (r_size) { + *r_size = mb->size; + } + break; + } + default: + return false; + } + return true; +} + +/** Get evaluated mesh for given object. */ +Mesh *BKE_object_get_evaluated_mesh(const Object *object) +{ + ID *data_eval = object->runtime.data_eval; + return (data_eval && GS(data_eval->name) == ID_ME) ? (Mesh *)data_eval : nullptr; +} + +/** + * Get mesh which is not affected by modifiers: + * - For original objects it will be same as `object->data`, and it is a mesh + * which is in the corresponding #Main. + * - For copied-on-write objects it will give pointer to a copied-on-write + * mesh which corresponds to original object's mesh. + */ +Mesh *BKE_object_get_pre_modified_mesh(const Object *object) +{ + if (object->type == OB_MESH && object->runtime.data_orig != nullptr) { + BLI_assert(object->id.tag & LIB_TAG_COPIED_ON_WRITE); + BLI_assert(object->id.orig_id != nullptr); + BLI_assert(object->runtime.data_orig->orig_id == ((Object *)object->id.orig_id)->data); + Mesh *result = (Mesh *)object->runtime.data_orig; + BLI_assert((result->id.tag & LIB_TAG_COPIED_ON_WRITE) != 0); + BLI_assert((result->id.tag & LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT) == 0); + return result; + } + BLI_assert((object->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0); + return (Mesh *)object->data; +} + +/** + * Get a mesh which corresponds to the very original mesh from #Main. + * - For original objects it will be object->data. + * - For evaluated objects it will be same mesh as corresponding original + * object uses as data. + */ +Mesh *BKE_object_get_original_mesh(const Object *object) +{ + Mesh *result = nullptr; + if (object->id.orig_id == nullptr) { + BLI_assert((object->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0); + result = (Mesh *)object->data; + } + else { + BLI_assert((object->id.tag & LIB_TAG_COPIED_ON_WRITE) != 0); + result = (Mesh *)((Object *)object->id.orig_id)->data; + } + BLI_assert(result != nullptr); + BLI_assert((result->id.tag & (LIB_TAG_COPIED_ON_WRITE | LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT)) == + 0); + return result; +} + +Lattice *BKE_object_get_lattice(const Object *object) +{ + ID *data = (ID *)object->data; + if (data == nullptr || GS(data->name) != ID_LT) { + return nullptr; + } + + Lattice *lt = (Lattice *)data; + if (lt->editlatt) { + return lt->editlatt->latt; + } + + return lt; +} + +Lattice *BKE_object_get_evaluated_lattice(const Object *object) +{ + ID *data_eval = object->runtime.data_eval; + + if (data_eval == nullptr || GS(data_eval->name) != ID_LT) { + return nullptr; + } + + Lattice *lt_eval = (Lattice *)data_eval; + if (lt_eval->editlatt) { + return lt_eval->editlatt->latt; + } + + return lt_eval; +} + +static int pc_cmp(const void *a, const void *b) +{ + const LinkData *ad = (const LinkData *)a, *bd = (const LinkData *)b; + if (POINTER_AS_INT(ad->data) > POINTER_AS_INT(bd->data)) { + return 1; + } + + return 0; +} + +/* TODO: Review the usages of this function, currently with COW it will be called for orig object + * and then again for COW copies of it, think this is bad since there is no guarantee that we get + * the same stack index in both cases? Order is important since this index is used for filenames on + * disk. */ +int BKE_object_insert_ptcache(Object *ob) +{ + LinkData *link = nullptr; + int i = 0; + + BLI_listbase_sort(&ob->pc_ids, pc_cmp); + + for (link = (LinkData *)ob->pc_ids.first, i = 0; link; link = link->next, i++) { + int index = POINTER_AS_INT(link->data); + + if (i < index) { + break; + } + } + + link = (LinkData *)MEM_callocN(sizeof(LinkData), "PCLink"); + link->data = POINTER_FROM_INT(i); + BLI_addtail(&ob->pc_ids, link); + + return i; +} + +static int pc_findindex(ListBase *listbase, int index) +{ + int number = 0; + + if (listbase == nullptr) { + return -1; + } + + LinkData *link = (LinkData *)listbase->first; + while (link) { + if (POINTER_AS_INT(link->data) == index) { + return number; + } + + number++; + link = link->next; + } + + return -1; +} + +void BKE_object_delete_ptcache(Object *ob, int index) +{ + int list_index = pc_findindex(&ob->pc_ids, index); + LinkData *link = (LinkData *)BLI_findlink(&ob->pc_ids, list_index); + BLI_freelinkN(&ob->pc_ids, link); +} + +/* -------------------------------------------------------------------- */ +/** \name Object Data Shape Key Insert + * \{ */ + +/** Mesh */ +static KeyBlock *insert_meshkey(Main *bmain, Object *ob, const char *name, const bool from_mix) +{ + Mesh *me = (Mesh *)ob->data; + Key *key = me->key; + KeyBlock *kb; + int newkey = 0; + + if (key == nullptr) { + key = me->key = BKE_key_add(bmain, (ID *)me); + key->type = KEY_RELATIVE; + newkey = 1; + } + + if (newkey || from_mix == false) { + /* create from mesh */ + kb = BKE_keyblock_add_ctime(key, name, false); + BKE_keyblock_convert_from_mesh(me, key, kb); + } + else { + /* copy from current values */ + int totelem; + float *data = BKE_key_evaluate_object(ob, &totelem); + + /* create new block with prepared data */ + kb = BKE_keyblock_add_ctime(key, name, false); + kb->data = data; + kb->totelem = totelem; + } + + return kb; +} +/** Lattice */ +static KeyBlock *insert_lattkey(Main *bmain, Object *ob, const char *name, const bool from_mix) +{ + Lattice *lt = (Lattice *)ob->data; + Key *key = lt->key; + KeyBlock *kb; + int newkey = 0; + + if (key == nullptr) { + key = lt->key = BKE_key_add(bmain, (ID *)lt); + key->type = KEY_RELATIVE; + newkey = 1; + } + + if (newkey || from_mix == false) { + kb = BKE_keyblock_add_ctime(key, name, false); + if (!newkey) { + KeyBlock *basekb = (KeyBlock *)key->block.first; + kb->data = MEM_dupallocN(basekb->data); + kb->totelem = basekb->totelem; + } + else { + BKE_keyblock_convert_from_lattice(lt, kb); + } + } + else { + /* copy from current values */ + int totelem; + float *data = BKE_key_evaluate_object(ob, &totelem); + + /* create new block with prepared data */ + kb = BKE_keyblock_add_ctime(key, name, false); + kb->totelem = totelem; + kb->data = data; + } + + return kb; +} +/** Curve */ +static KeyBlock *insert_curvekey(Main *bmain, Object *ob, const char *name, const bool from_mix) +{ + Curve *cu = (Curve *)ob->data; + Key *key = cu->key; + KeyBlock *kb; + ListBase *lb = BKE_curve_nurbs_get(cu); + int newkey = 0; + + if (key == nullptr) { + key = cu->key = BKE_key_add(bmain, (ID *)cu); + key->type = KEY_RELATIVE; + newkey = 1; + } + + if (newkey || from_mix == false) { + /* create from curve */ + kb = BKE_keyblock_add_ctime(key, name, false); + if (!newkey) { + KeyBlock *basekb = (KeyBlock *)key->block.first; + kb->data = MEM_dupallocN(basekb->data); + kb->totelem = basekb->totelem; + } + else { + BKE_keyblock_convert_from_curve(cu, kb, lb); + } + } + else { + /* copy from current values */ + int totelem; + float *data = BKE_key_evaluate_object(ob, &totelem); + + /* create new block with prepared data */ + kb = BKE_keyblock_add_ctime(key, name, false); + kb->totelem = totelem; + kb->data = data; + } + + return kb; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Shape Key API + * \{ */ + +KeyBlock *BKE_object_shapekey_insert(Main *bmain, + Object *ob, + const char *name, + const bool from_mix) +{ + KeyBlock *key = nullptr; + + switch (ob->type) { + case OB_MESH: + key = insert_meshkey(bmain, ob, name, from_mix); + break; + case OB_CURVE: + case OB_SURF: + key = insert_curvekey(bmain, ob, name, from_mix); + break; + case OB_LATTICE: + key = insert_lattkey(bmain, ob, name, from_mix); + break; + default: + break; + } + + /* Set the first active when none is set when called from RNA. */ + if (key != nullptr) { + if (ob->shapenr <= 0) { + ob->shapenr = 1; + } + } + + return key; +} + +bool BKE_object_shapekey_free(Main *bmain, Object *ob) +{ + Key **key_p, *key; + + key_p = BKE_key_from_object_p(ob); + if (ELEM(nullptr, key_p, *key_p)) { + return false; + } + + key = *key_p; + *key_p = nullptr; + + BKE_id_free_us(bmain, key); + + return true; +} + +bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb) +{ + Key *key = BKE_key_from_object(ob); + short kb_index; + + if (key == nullptr) { + return false; + } + + kb_index = BLI_findindex(&key->block, kb); + BLI_assert(kb_index != -1); + + LISTBASE_FOREACH (KeyBlock *, rkb, &key->block) { + if (rkb->relative == kb_index) { + /* remap to the 'Basis' */ + rkb->relative = 0; + } + else if (rkb->relative >= kb_index) { + /* Fix positional shift of the keys when kb is deleted from the list */ + rkb->relative -= 1; + } + } + + BLI_remlink(&key->block, kb); + key->totkey--; + if (key->refkey == kb) { + key->refkey = (KeyBlock *)key->block.first; + + if (key->refkey) { + /* apply new basis key on original data */ + switch (ob->type) { + case OB_MESH: + BKE_keyblock_convert_to_mesh(key->refkey, (Mesh *)ob->data); + break; + case OB_CURVE: + case OB_SURF: + BKE_keyblock_convert_to_curve( + key->refkey, (Curve *)ob->data, BKE_curve_nurbs_get((Curve *)ob->data)); + break; + case OB_LATTICE: + BKE_keyblock_convert_to_lattice(key->refkey, (Lattice *)ob->data); + break; + } + } + } + + if (kb->data) { + MEM_freeN(kb->data); + } + MEM_freeN(kb); + + /* Unset active when all are freed. */ + if (BLI_listbase_is_empty(&key->block)) { + ob->shapenr = 0; + } + else if (ob->shapenr > 1) { + ob->shapenr--; + } + + if (key->totkey == 0) { + BKE_object_shapekey_free(bmain, ob); + } + + return true; +} + +/** \} */ + +bool BKE_object_flag_test_recursive(const Object *ob, short flag) +{ + if (ob->flag & flag) { + return true; + } + if (ob->parent) { + return BKE_object_flag_test_recursive(ob->parent, flag); + } + + return false; +} + +bool BKE_object_is_child_recursive(const Object *ob_parent, const Object *ob_child) +{ + for (ob_child = ob_child->parent; ob_child; ob_child = ob_child->parent) { + if (ob_child == ob_parent) { + return true; + } + } + return false; +} + +/** + * Most important if this is modified it should _always_ return true, in certain + * cases false positives are hard to avoid (shape keys for example). + */ +int BKE_object_is_modified(Scene *scene, Object *ob) +{ + /* Always test on original object since evaluated object may no longer + * have shape keys or modifiers that were used to evaluate it. */ + ob = DEG_get_original_object(ob); + + int flag = 0; + + if (BKE_key_from_object(ob)) { + flag |= eModifierMode_Render | eModifierMode_Realtime; + } + else { + ModifierData *md; + VirtualModifierData virtualModifierData; + /* cloth */ + for (md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); + md && (flag != (eModifierMode_Render | eModifierMode_Realtime)); + md = md->next) { + if ((flag & eModifierMode_Render) == 0 && + BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) { + flag |= eModifierMode_Render; + } + + if ((flag & eModifierMode_Realtime) == 0 && + BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { + flag |= eModifierMode_Realtime; + } + } + } + + return flag; +} + +/** + * Check of objects moves in time. + * + * \note This function is currently optimized for usage in combination + * with modifier deformation checks (#eModifierTypeType_OnlyDeform), + * so modifiers can quickly check if their target objects moves + * (causing deformation motion blur) or not. + * + * This makes it possible to give some degree of false-positives here, + * but it's currently an acceptable tradeoff between complexity and check + * speed. In combination with checks of modifier stack and real life usage + * percentage of false-positives shouldn't be that high. + * + * \note This function does not consider physics systems. + */ +bool BKE_object_moves_in_time(const Object *object, bool recurse_parent) +{ + /* If object has any sort of animation data assume it is moving. */ + if (BKE_animdata_id_is_animated(&object->id)) { + return true; + } + if (!BLI_listbase_is_empty(&object->constraints)) { + return true; + } + if (recurse_parent && object->parent != nullptr) { + return BKE_object_moves_in_time(object->parent, true); + } + return false; +} + +static bool object_moves_in_time(const Object *object) +{ + return BKE_object_moves_in_time(object, true); +} + +static bool object_deforms_in_time(Object *object) +{ + if (BKE_key_from_object(object) != nullptr) { + return true; + } + if (!BLI_listbase_is_empty(&object->modifiers)) { + return true; + } + return object_moves_in_time(object); +} + +static bool constructive_modifier_is_deform_modified(Object *ob, ModifierData *md) +{ + /* TODO(sergey): Consider generalizing this a bit so all modifier logic + * is concentrated in MOD_{modifier}.c file, + */ + if (md->type == eModifierType_Array) { + ArrayModifierData *amd = (ArrayModifierData *)md; + /* TODO(sergey): Check if curve is deformed. */ + return (amd->start_cap != nullptr && object_moves_in_time(amd->start_cap)) || + (amd->end_cap != nullptr && object_moves_in_time(amd->end_cap)) || + (amd->curve_ob != nullptr && object_moves_in_time(amd->curve_ob)) || + (amd->offset_ob != nullptr && object_moves_in_time(amd->offset_ob)); + } + if (md->type == eModifierType_Mirror) { + MirrorModifierData *mmd = (MirrorModifierData *)md; + return mmd->mirror_ob != nullptr && + (object_moves_in_time(mmd->mirror_ob) || object_moves_in_time(ob)); + } + if (md->type == eModifierType_Screw) { + ScrewModifierData *smd = (ScrewModifierData *)md; + return smd->ob_axis != nullptr && object_moves_in_time(smd->ob_axis); + } + if (md->type == eModifierType_MeshSequenceCache) { + /* NOTE: Not ideal because it's unknown whether topology changes or not. + * This will be detected later, so by assuming it's only deformation + * going on here we allow baking deform-only mesh to Alembic and have + * proper motion blur after that. + */ + return true; + } + if (md->type == eModifierType_Nodes) { + /* Not ideal for performance to always assume this is animated, + * but hard to detect in general. The better long term solution is likely + * to replace BKE_object_is_deform_modified by a test if the object was + * modified by the depsgraph when changing frames. */ + return true; + } + return false; +} + +static bool modifiers_has_animation_check(const Object *ob) +{ + /* TODO(sergey): This is a bit code duplication with depsgraph, but + * would be nicer to solve this as a part of new dependency graph + * work, so we avoid conflicts and so. + */ + if (ob->adt != nullptr) { + AnimData *adt = ob->adt; + if (adt->action != nullptr) { + LISTBASE_FOREACH (FCurve *, fcu, &adt->action->curves) { + if (fcu->rna_path && strstr(fcu->rna_path, "modifiers[")) { + return true; + } + } + } + LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { + if (fcu->rna_path && strstr(fcu->rna_path, "modifiers[")) { + return true; + } + } + } + return false; +} + +/** + * Test if object is affected by deforming modifiers (for motion blur). again + * most important is to avoid false positives, this is to skip computations + * and we can still if there was actual deformation afterwards. + */ +int BKE_object_is_deform_modified(Scene *scene, Object *ob) +{ + /* Always test on original object since evaluated object may no longer + * have shape keys or modifiers that were used to evaluate it. */ + ob = DEG_get_original_object(ob); + + ModifierData *md; + VirtualModifierData virtualModifierData; + int flag = 0; + const bool is_modifier_animated = modifiers_has_animation_check(ob); + + if (BKE_key_from_object(ob)) { + flag |= eModifierMode_Realtime | eModifierMode_Render; + } + + if (ob->type == OB_CURVE) { + Curve *cu = (Curve *)ob->data; + if (cu->taperobj != nullptr && object_deforms_in_time(cu->taperobj)) { + flag |= eModifierMode_Realtime | eModifierMode_Render; + } + } + + /* cloth */ + for (md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); + md && (flag != (eModifierMode_Render | eModifierMode_Realtime)); + md = md->next) { + const ModifierTypeInfo *mti = BKE_modifier_get_info((const ModifierType)md->type); + bool can_deform = mti->type == eModifierTypeType_OnlyDeform || is_modifier_animated; + + if (!can_deform) { + can_deform = constructive_modifier_is_deform_modified(ob, md); + } + + if (can_deform) { + if (!(flag & eModifierMode_Render) && + BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) { + flag |= eModifierMode_Render; + } + + if (!(flag & eModifierMode_Realtime) && + BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { + flag |= eModifierMode_Realtime; + } + } + } + + return flag; +} + +/** Return the number of scenes using (instantiating) that object in their collections. */ +int BKE_object_scenes_users_get(Main *bmain, Object *ob) +{ + int num_scenes = 0; + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (BKE_collection_has_object_recursive(scene->master_collection, ob)) { + num_scenes++; + } + } + return num_scenes; +} + +MovieClip *BKE_object_movieclip_get(Scene *scene, Object *ob, bool use_default) +{ + MovieClip *clip = use_default ? scene->clip : nullptr; + bConstraint *con = (bConstraint *)ob->constraints.first, *scon = nullptr; + + while (con) { + if (con->type == CONSTRAINT_TYPE_CAMERASOLVER) { + if (scon == nullptr || (scon->flag & CONSTRAINT_OFF)) { + scon = con; + } + } + + con = con->next; + } + + if (scon) { + bCameraSolverConstraint *solver = (bCameraSolverConstraint *)scon->data; + if ((solver->flag & CAMERASOLVER_ACTIVECLIP) == 0) { + clip = solver->clip; + } + else { + clip = scene->clip; + } + } + + return clip; +} + +void BKE_object_runtime_reset(Object *object) +{ + memset(&object->runtime, 0, sizeof(object->runtime)); +} + +/** + * Reset all pointers which we don't want to be shared when copying the object. + */ +void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag)) +{ + Object_Runtime *runtime = &object->runtime; + runtime->data_eval = nullptr; + runtime->gpd_eval = nullptr; + runtime->mesh_deform_eval = nullptr; + runtime->curve_cache = nullptr; + runtime->object_as_temp_mesh = nullptr; + runtime->object_as_temp_curve = nullptr; + runtime->geometry_set_eval = nullptr; +} + +/** + * The function frees memory used by the runtime data, but not the runtime field itself. + * + * All runtime data is cleared to ensure it's not used again, + * in keeping with other `_free_data(..)` functions. + */ +void BKE_object_runtime_free_data(Object *object) +{ + /* Currently this is all that's needed. */ + BKE_object_free_derived_caches(object); + + BKE_object_runtime_reset(object); +} + +/** + * Find an associated armature object. + */ +static Object *obrel_armature_find(Object *ob) +{ + Object *ob_arm = nullptr; + + if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) { + ob_arm = ob->parent; + } + else { + ModifierData *mod; + for (mod = (ModifierData *)ob->modifiers.first; mod; mod = mod->next) { + if (mod->type == eModifierType_Armature) { + ob_arm = ((ArmatureModifierData *)mod)->object; + } + } + } + + return ob_arm; +} + +static bool obrel_list_test(Object *ob) +{ + return ob && !(ob->id.tag & LIB_TAG_DOIT); +} + +static void obrel_list_add(LinkNode **links, Object *ob) +{ + BLI_linklist_prepend(links, ob); + ob->id.tag |= LIB_TAG_DOIT; +} + +/** + * Iterates over all objects of the given scene layer. + * Depending on the #eObjectSet flag: + * collect either #OB_SET_ALL, #OB_SET_VISIBLE or #OB_SET_SELECTED objects. + * If #OB_SET_VISIBLE or#OB_SET_SELECTED are collected, + * then also add related objects according to the given \a includeFilter. + */ +LinkNode *BKE_object_relational_superset(struct ViewLayer *view_layer, + eObjectSet objectSet, + eObRelationTypes includeFilter) +{ + LinkNode *links = nullptr; + + /* Remove markers from all objects */ + LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { + base->object->id.tag &= ~LIB_TAG_DOIT; + } + + /* iterate over all selected and visible objects */ + LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { + if (objectSet == OB_SET_ALL) { + /* as we get all anyways just add it */ + Object *ob = base->object; + obrel_list_add(&links, ob); + } + else { + if ((objectSet == OB_SET_SELECTED && BASE_SELECTED_EDITABLE(((View3D *)nullptr), base)) || + (objectSet == OB_SET_VISIBLE && BASE_EDITABLE(((View3D *)nullptr), base))) { + Object *ob = base->object; + + if (obrel_list_test(ob)) { + obrel_list_add(&links, ob); + } + + /* parent relationship */ + if (includeFilter & (OB_REL_PARENT | OB_REL_PARENT_RECURSIVE)) { + Object *parent = ob->parent; + if (obrel_list_test(parent)) { + + obrel_list_add(&links, parent); + + /* recursive parent relationship */ + if (includeFilter & OB_REL_PARENT_RECURSIVE) { + parent = parent->parent; + while (obrel_list_test(parent)) { + + obrel_list_add(&links, parent); + parent = parent->parent; + } + } + } + } + + /* child relationship */ + if (includeFilter & (OB_REL_CHILDREN | OB_REL_CHILDREN_RECURSIVE)) { + LISTBASE_FOREACH (Base *, local_base, &view_layer->object_bases) { + if (BASE_EDITABLE(((View3D *)nullptr), local_base)) { + + Object *child = local_base->object; + if (obrel_list_test(child)) { + if ((includeFilter & OB_REL_CHILDREN_RECURSIVE && + BKE_object_is_child_recursive(ob, child)) || + (includeFilter & OB_REL_CHILDREN && child->parent && child->parent == ob)) { + obrel_list_add(&links, child); + } + } + } + } + } + + /* include related armatures */ + if (includeFilter & OB_REL_MOD_ARMATURE) { + Object *arm = obrel_armature_find(ob); + if (obrel_list_test(arm)) { + obrel_list_add(&links, arm); + } + } + } + } + } + + return links; +} + +/** + * return all groups this object is a part of, caller must free. + */ +struct LinkNode *BKE_object_groups(Main *bmain, Scene *scene, Object *ob) +{ + LinkNode *collection_linknode = nullptr; + Collection *collection = nullptr; + while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) { + BLI_linklist_prepend(&collection_linknode, collection); + } + + return collection_linknode; +} + +void BKE_object_groups_clear(Main *bmain, Scene *scene, Object *ob) +{ + Collection *collection = nullptr; + while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) { + BKE_collection_object_remove(bmain, collection, ob, false); + DEG_id_tag_update(&collection->id, ID_RECALC_COPY_ON_WRITE); + } +} + +/** + * Return a KDTree_3d from the deformed object (in worldspace) + * + * \note Only mesh objects currently support deforming, others are TODO. + * + * \param ob: + * \param r_tot: + * \return The kdtree or nullptr if it can't be created. + */ +KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot) +{ + KDTree_3d *tree = nullptr; + unsigned int tot = 0; + + switch (ob->type) { + case OB_MESH: { + Mesh *me = (Mesh *)ob->data; + unsigned int i; + + Mesh *me_eval = ob->runtime.mesh_deform_eval ? ob->runtime.mesh_deform_eval : + BKE_object_get_evaluated_mesh(ob); + const int *index; + + if (me_eval && (index = (const int *)CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX))) { + MVert *mvert = me_eval->mvert; + uint totvert = me_eval->totvert; + + /* tree over-allocs in case where some verts have ORIGINDEX_NONE */ + tot = 0; + tree = BLI_kdtree_3d_new(totvert); + + /* We don't how many verts from the DM we can use. */ + for (i = 0; i < totvert; i++) { + if (index[i] != ORIGINDEX_NONE) { + float co[3]; + mul_v3_m4v3(co, ob->obmat, mvert[i].co); + BLI_kdtree_3d_insert(tree, index[i], co); + tot++; + } + } + } + else { + MVert *mvert = me->mvert; + + tot = me->totvert; + tree = BLI_kdtree_3d_new(tot); + + for (i = 0; i < tot; i++) { + float co[3]; + mul_v3_m4v3(co, ob->obmat, mvert[i].co); + BLI_kdtree_3d_insert(tree, i, co); + } + } + + BLI_kdtree_3d_balance(tree); + break; + } + case OB_CURVE: + case OB_SURF: { + /* TODO: take deformation into account */ + Curve *cu = (Curve *)ob->data; + unsigned int i, a; + + Nurb *nu; + + tot = BKE_nurbList_verts_count_without_handles(&cu->nurb); + tree = BLI_kdtree_3d_new(tot); + i = 0; + + nu = (Nurb *)cu->nurb.first; + while (nu) { + if (nu->bezt) { + BezTriple *bezt; + + bezt = nu->bezt; + a = nu->pntsu; + while (a--) { + float co[3]; + mul_v3_m4v3(co, ob->obmat, bezt->vec[1]); + BLI_kdtree_3d_insert(tree, i++, co); + bezt++; + } + } + else { + BPoint *bp; + + bp = nu->bp; + a = nu->pntsu * nu->pntsv; + while (a--) { + float co[3]; + mul_v3_m4v3(co, ob->obmat, bp->vec); + BLI_kdtree_3d_insert(tree, i++, co); + bp++; + } + } + nu = nu->next; + } + + BLI_kdtree_3d_balance(tree); + break; + } + case OB_LATTICE: { + /* TODO: take deformation into account */ + Lattice *lt = (Lattice *)ob->data; + BPoint *bp; + unsigned int i; + + tot = lt->pntsu * lt->pntsv * lt->pntsw; + tree = BLI_kdtree_3d_new(tot); + i = 0; + + for (bp = lt->def; i < tot; bp++) { + float co[3]; + mul_v3_m4v3(co, ob->obmat, bp->vec); + BLI_kdtree_3d_insert(tree, i++, co); + } + + BLI_kdtree_3d_balance(tree); + break; + } + } + + *r_tot = tot; + return tree; +} + +bool BKE_object_modifier_use_time(Scene *scene, + Object *ob, + ModifierData *md, + const int dag_eval_mode) +{ + if (BKE_modifier_depends_ontime(scene, md, dag_eval_mode)) { + return true; + } + + /* Check whether modifier is animated. */ + /* TODO: this should be handled as part of build_animdata() -- Aligorith */ + if (ob->adt) { + AnimData *adt = ob->adt; + FCurve *fcu; + + char md_name_esc[sizeof(md->name) * 2]; + BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc)); + + char pattern[sizeof(md_name_esc) + 16]; + BLI_snprintf(pattern, sizeof(pattern), "modifiers[\"%s\"]", md_name_esc); + + /* action - check for F-Curves with paths containing 'modifiers[' */ + if (adt->action) { + for (fcu = (FCurve *)adt->action->curves.first; fcu != nullptr; fcu = (FCurve *)fcu->next) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { + return true; + } + } + } + + /* This here allows modifier properties to get driven and still update properly + * + * Workaround to get T26764 (e.g. subsurf levels not updating when animated/driven) + * working, without the updating problems (T28525 T28690 T28774 T28777) caused + * by the RNA updates cache introduced in r.38649 + */ + for (fcu = (FCurve *)adt->drivers.first; fcu != nullptr; fcu = (FCurve *)fcu->next) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { + return true; + } + } + + /* XXX: also, should check NLA strips, though for now assume that nobody uses + * that and we can omit that for performance reasons... */ + } + + return false; +} + +bool BKE_object_modifier_gpencil_use_time(Object *ob, GpencilModifierData *md) +{ + if (BKE_gpencil_modifier_depends_ontime(md)) { + return true; + } + + /* Check whether modifier is animated. */ + /* TODO(Aligorith): this should be handled as part of build_animdata() */ + if (ob->adt) { + AnimData *adt = ob->adt; + + char md_name_esc[sizeof(md->name) * 2]; + BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc)); + + char pattern[sizeof(md_name_esc) + 32]; + BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md_name_esc); + + /* action - check for F-Curves with paths containing 'grease_pencil_modifiers[' */ + if (adt->action) { + LISTBASE_FOREACH (FCurve *, fcu, &adt->action->curves) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { + return true; + } + } + } + + /* This here allows modifier properties to get driven and still update properly */ + LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { + return true; + } + } + } + + return false; +} + +bool BKE_object_shaderfx_use_time(Object *ob, ShaderFxData *fx) +{ + if (BKE_shaderfx_depends_ontime(fx)) { + return true; + } + + /* Check whether effect is animated. */ + /* TODO(Aligorith): this should be handled as part of build_animdata() */ + if (ob->adt) { + AnimData *adt = ob->adt; + + char fx_name_esc[sizeof(fx->name) * 2]; + BLI_str_escape(fx_name_esc, fx->name, sizeof(fx_name_esc)); + + char pattern[sizeof(fx_name_esc) + 32]; + BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx_name_esc); + + /* action - check for F-Curves with paths containing string[' */ + if (adt->action) { + LISTBASE_FOREACH (FCurve *, fcu, &adt->action->curves) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { + return true; + } + } + } + + /* This here allows properties to get driven and still update properly */ + LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) { + return true; + } + } + } + + return false; +} + +/** + * Set "ignore cache" flag for all caches on this object. + */ +static void object_cacheIgnoreClear(Object *ob, int state) +{ + ListBase pidlist; + BKE_ptcache_ids_from_object(&pidlist, ob, nullptr, 0); + + LISTBASE_FOREACH (PTCacheID *, pid, &pidlist) { + if (pid->cache) { + if (state) { + pid->cache->flag |= PTCACHE_IGNORE_CLEAR; + } + else { + pid->cache->flag &= ~PTCACHE_IGNORE_CLEAR; + } + } + } + + BLI_freelistN(&pidlist); +} + +/** + * \note this function should eventually be replaced by depsgraph functionality. + * Avoid calling this in new code unless there is a very good reason for it! + */ +bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + bool update_mesh, + int parent_recursion, + float frame, + int type) +{ + const bool flush_to_original = DEG_is_active(depsgraph); + ModifierData *md = BKE_modifiers_findby_type(ob, (ModifierType)type); + + if (type == eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; + + /* if other is dynamic paint canvas, don't update */ + if (pmd && pmd->canvas) { + return true; + } + } + else if (type == eModifierType_Fluid) { + FluidModifierData *fmd = (FluidModifierData *)md; + + if (fmd && (fmd->type & MOD_FLUID_TYPE_DOMAIN) != 0) { + return true; + } + } + + /* if object has parents, update them too */ + if (parent_recursion) { + int recursion = parent_recursion - 1; + bool no_update = false; + if (ob->parent) { + no_update |= BKE_object_modifier_update_subframe( + depsgraph, scene, ob->parent, false, recursion, frame, type); + } + if (ob->track) { + no_update |= BKE_object_modifier_update_subframe( + depsgraph, scene, ob->track, false, recursion, frame, type); + } + + /* skip subframe if object is parented + * to vertex of a dynamic paint canvas */ + if (no_update && (ELEM(ob->partype, PARVERT1, PARVERT3))) { + return false; + } + + /* also update constraint targets */ + LISTBASE_FOREACH (bConstraint *, con, &ob->constraints) { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + ListBase targets = {nullptr, nullptr}; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) { + if (ct->tar) { + BKE_object_modifier_update_subframe( + depsgraph, scene, ct->tar, false, recursion, frame, type); + } + } + /* free temp targets */ + if (cti->flush_constraint_targets) { + cti->flush_constraint_targets(con, &targets, false); + } + } + } + } + + /* was originally ID_RECALC_ALL - TODO: which flags are really needed??? */ + /* TODO(sergey): What about animation? */ + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, + frame); + + ob->id.recalc |= ID_RECALC_ALL; + if (update_mesh) { + BKE_animsys_evaluate_animdata( + &ob->id, ob->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); + /* ignore cache clear during subframe updates + * to not mess up cache validity */ + object_cacheIgnoreClear(ob, 1); + BKE_object_handle_update(depsgraph, scene, ob); + object_cacheIgnoreClear(ob, 0); + } + else { + BKE_object_where_is_calc_time(depsgraph, scene, ob, frame); + } + + /* for curve following objects, parented curve has to be updated too */ + if (ob->type == OB_CURVE) { + Curve *cu = (Curve *)ob->data; + BKE_animsys_evaluate_animdata( + &cu->id, cu->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); + } + /* and armatures... */ + if (ob->type == OB_ARMATURE) { + bArmature *arm = (bArmature *)ob->data; + BKE_animsys_evaluate_animdata( + &arm->id, arm->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); + BKE_pose_where_is(depsgraph, scene, ob); + } + + return false; +} + +/** + * Updates select_id of all objects in the given \a bmain. + */ +void BKE_object_update_select_id(struct Main *bmain) +{ + Object *ob = (Object *)bmain->objects.first; + int select_id = 1; + while (ob) { + ob->runtime.select_id = select_id++; + ob = (Object *)ob->id.next; + } +} + +Mesh *BKE_object_to_mesh(Depsgraph *depsgraph, Object *object, bool preserve_all_data_layers) +{ + BKE_object_to_mesh_clear(object); + + Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false); + object->runtime.object_as_temp_mesh = mesh; + return mesh; +} + +void BKE_object_to_mesh_clear(Object *object) +{ + if (object->runtime.object_as_temp_mesh == nullptr) { + return; + } + BKE_id_free(nullptr, object->runtime.object_as_temp_mesh); + object->runtime.object_as_temp_mesh = nullptr; +} + +Curve *BKE_object_to_curve(Object *object, Depsgraph *depsgraph, bool apply_modifiers) +{ + BKE_object_to_curve_clear(object); + + Curve *curve = BKE_curve_new_from_object(object, depsgraph, apply_modifiers); + object->runtime.object_as_temp_curve = curve; + return curve; +} + +void BKE_object_to_curve_clear(Object *object) +{ + if (object->runtime.object_as_temp_curve == nullptr) { + return; + } + BKE_id_free(nullptr, object->runtime.object_as_temp_curve); + object->runtime.object_as_temp_curve = nullptr; +} + +void BKE_object_check_uuids_unique_and_report(const Object *object) +{ + BKE_pose_check_uuids_unique_and_report(object->pose); + BKE_modifier_check_uuids_unique_and_report(object); +} + +void BKE_object_modifiers_lib_link_common(void *userData, + struct Object *ob, + struct ID **idpoin, + int cb_flag) +{ + BlendLibReader *reader = (BlendLibReader *)userData; + + BLO_read_id_address(reader, ob->id.lib, idpoin); + if (*idpoin != nullptr && (cb_flag & IDWALK_CB_USER) != 0) { + id_us_plus_no_lib(*idpoin); + } +} + +void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data) +{ + ob->type = BKE_object_obdata_to_type(new_data); + ob->data = (void *)new_data; + ob->runtime.geometry_set_eval = nullptr; + ob->runtime.data_eval = nullptr; + if (ob->runtime.bb != nullptr) { + ob->runtime.bb->flag |= BOUNDBOX_DIRTY; + } + ob->id.py_instance = nullptr; +} + +bool BKE_object_supports_material_slots(struct Object *ob) +{ + return ELEM(ob->type, + OB_MESH, + OB_CURVE, + OB_SURF, + OB_FONT, + OB_MBALL, + OB_HAIR, + OB_POINTCLOUD, + OB_VOLUME, + OB_GPENCIL); +} diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h index fcaac4ded76..0aa4a171511 100644 --- a/source/blender/makesdna/DNA_object_force_types.h +++ b/source/blender/makesdna/DNA_object_force_types.h @@ -30,6 +30,8 @@ extern "C" { #endif +struct BodySpring; + /* pd->forcefield: Effector Fields types */ typedef enum ePFieldType { /** (this is used for general effector weight). */ -- cgit v1.2.3