/* * 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_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_font.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_volume.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" #include "DRW_engine.h" #include "BLO_read_write.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(!"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_defgroup_copy_list(&ob_dst->defbase, &ob_src->defbase); 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->defbase); 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) { 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 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 (!ID_IS_LINKED(ob)) { return; } BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); if (lib_local || is_local) { if (!is_lib) { BKE_lib_id_clear_library_data(bmain, &ob->id); BKE_lib_id_expand_local(bmain, &ob->id); 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 { 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(data, object->parent, IDWALK_CB_NEVER_SELF); BKE_LIB_FOREACHID_PROCESS(data, object->track, IDWALK_CB_NEVER_SELF); /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ BKE_LIB_FOREACHID_PROCESS(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); BKE_LIB_FOREACHID_PROCESS(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(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(data, object->poselib, IDWALK_CB_USER); for (int i = 0; i < object->totcol; i++) { BKE_LIB_FOREACHID_PROCESS(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(data, object->instance_collection, IDWALK_CB_USER); if (object->pd) { BKE_LIB_FOREACHID_PROCESS(data, object->pd->tex, IDWALK_CB_USER); BKE_LIB_FOREACHID_PROCESS(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(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(data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); BKE_LIB_FOREACHID_PROCESS(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(data, object->soft->collision_group, IDWALK_CB_NOP); if (object->soft->effector_weights) { BKE_LIB_FOREACHID_PROCESS(data, object->soft->effector_weights->group, IDWALK_CB_NOP); } } } static void write_defgroups(BlendWriter *writer, ListBase *defbase) { LISTBASE_FOREACH (bDeformGroup *, defgroup, defbase) { BLO_write_struct(writer, bDeformGroup, defgroup); } } 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); if (ob->id.us > 0 || is_undo) { /* 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_defgroups(writer, &ob->defbase); 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); } 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; bool warn = false; /* 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(BLO_read_lib_reports(reader), RPT_WARNING, 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 (ob->proxy->id.lib == NULL) { ob->proxy->proxy_from = NULL; ob->proxy = NULL; if (ob->id.lib) { printf("Proxy lost from object %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath); } else { printf("Proxy lost from object %s lib \n", ob->id.name + 2); } } 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) { if (ob->id.lib) { printf("Can't find obdata of %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath); } else { printf("Object %s lost data.\n", ob->id.name + 2); } ob->type = OB_EMPTY; warn = true; 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; } } 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) { const short *totcol_data = BKE_object_material_len_p(ob); /* Only expand so as not to lose any object materials that might be set. */ if (totcol_data && (*totcol_data > ob->totcol)) { /* printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); */ BKE_object_material_resize(BLO_read_lib_get_main(reader), ob, *totcol_data, false); } } 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); } if (warn) { BLO_reportf_wrap(BLO_read_lib_reports(reader), RPT_WARNING, "Warning in console"); } } /* 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); /* 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 *UNUSED(id_src)) { Object *object = (Object *)id_dst; ListBase pidlist; BKE_ptcache_ids_from_object(&pidlist, object, NULL, 0); LISTBASE_FOREACH (PTCacheID *, pid, &pidlist) { LISTBASE_FOREACH (PointCache *, point_cache, pid->ptcaches) { point_cache->flag |= PTCACHE_FLAG_INFO_DIRTY; } } BLI_freelistN(&pidlist); } 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, }; 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); /* 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(!"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; } if (ob->runtime.geometry_set_previews != NULL) { BLI_ghash_free(ob->runtime.geometry_set_previews, NULL, (GHashValFreeFP)BKE_geometry_set_free); ob->runtime.geometry_set_previews = 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); } } /* Can be called from multiple threads. */ void BKE_object_preview_geometry_set_add(Object *ob, const uint64_t key, struct GeometrySet *geometry_set) { static ThreadMutex mutex = BLI_MUTEX_INITIALIZER; BLI_mutex_lock(&mutex); if (ob->runtime.geometry_set_previews == NULL) { ob->runtime.geometry_set_previews = BLI_ghash_int_new(__func__); } BLI_ghash_reinsert(ob->runtime.geometry_set_previews, POINTER_FROM_UINT(key), geometry_set, NULL, (GHashValFreeFP)BKE_geometry_set_free); BLI_mutex_unlock(&mutex); } /** * 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 (ob->runtime.geometry_set_eval != NULL && BKE_geometry_set_has_instances(ob->runtime.geometry_set_eval)) { 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; } } 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 - from reading existing code this seems correct but intended usage of * pointcache should /w cloth should be added in 'ParticleSystem' - campbell */ 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, const eLibIDDuplicateFlags duplicate_options) { const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; if (!is_subprocess) { BKE_main_id_newptr_and_tag_clear(bmain); /* 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; } } Material ***matarar; Object *obn = (Object *)BKE_id_copy_for_duplicate(bmain, &ob->id, dupflag); /* 0 == full linked. */ if (dupflag == 0) { return obn; } BKE_animdata_duplicate_id_action(bmain, &obn->id, dupflag); if (dupflag & USER_DUP_MAT) { for (int i = 0; i < obn->totcol; i++) { BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag); } } 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); } } 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); } break; case OB_CURVE: if (dupflag & USER_DUP_CURVE) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_SURF: if (dupflag & USER_DUP_SURF) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_FONT: if (dupflag & USER_DUP_FONT) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_MBALL: if (dupflag & USER_DUP_MBALL) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_LAMP: if (dupflag & USER_DUP_LAMP) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_ARMATURE: if (dupflag & USER_DUP_ARM) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_LATTICE: if (dupflag != 0) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_CAMERA: if (dupflag != 0) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_LIGHTPROBE: if (dupflag & USER_DUP_LIGHTPROBE) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_SPEAKER: if (dupflag != 0) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_GPENCIL: if (dupflag & USER_DUP_GPENCIL) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_HAIR: if (dupflag & USER_DUP_HAIR) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_POINTCLOUD: if (dupflag & USER_DUP_POINTCLOUD) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_VOLUME: if (dupflag & USER_DUP_VOLUME) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } 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); } } } } 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_add_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 vertex groups */ BKE_defgroup_copy_list(&ob->defbase, &target->defbase); /* 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_ERROR( &LOG, "Object %s with Bone parent: bone %s doesn't exist", ob->id.name + 2, ob->parsubstr); 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(!"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, 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->ok = 1; ob->iuser->flag |= IMA_ANIM_ALWAYS; ob->iuser->frames = 100; ob->iuser->sfra = 1; } } else { if (ob->iuser) { MEM_freeN(ob->iuser); ob->iuser = NULL; } } } 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; } 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 */ 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->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 : ob->runtime.mesh_deform_eval; 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(Object *ob, ModifierData *md) { if (BKE_modifier_depends_ontime(md)) { 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 && (ob->partype == PARVERT1 || ob->partype == 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); } }