/* SPDX-FileCopyrightText: 2014 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bke */ #include "MEM_guardedalloc.h" #include "DNA_customdata_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "BLI_listbase.h" #include "BLI_math_base.h" #include "BLI_math_matrix.h" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BKE_attribute.hh" #include "BKE_customdata.hh" #include "BKE_data_transfer.h" #include "BKE_deform.hh" #include "BKE_mesh.hh" #include "BKE_mesh_mapping.hh" #include "BKE_mesh_remap.hh" #include "BKE_mesh_runtime.hh" #include "BKE_mesh_wrapper.hh" #include "BKE_modifier.hh" #include "BKE_object.hh" #include "BKE_report.hh" #include "DEG_depsgraph_query.hh" #include "data_transfer_intern.hh" using blender::StringRef; void BKE_object_data_transfer_dttypes_to_cdmask(const int dtdata_types, CustomData_MeshMasks *r_data_masks) { for (int i = 0; i < DT_TYPE_MAX; i++) { const int dtdata_type = 1 << i; int cddata_type; if (!(dtdata_types & dtdata_type)) { continue; } cddata_type = BKE_object_data_transfer_dttype_to_cdtype(dtdata_type); if (!(cddata_type & CD_FAKE)) { if (DT_DATATYPE_IS_VERT(dtdata_type)) { r_data_masks->vmask |= 1LL << cddata_type; } else if (DT_DATATYPE_IS_EDGE(dtdata_type)) { r_data_masks->emask |= 1LL << cddata_type; } else if (DT_DATATYPE_IS_LOOP(dtdata_type)) { r_data_masks->lmask |= 1LL << cddata_type; } else if (DT_DATATYPE_IS_FACE(dtdata_type)) { r_data_masks->pmask |= 1LL << cddata_type; } } else if (cddata_type == CD_FAKE_MDEFORMVERT) { r_data_masks->vmask |= CD_MASK_MDEFORMVERT; /* Exception for vgroups :/ */ } else if (cddata_type == CD_FAKE_UV) { r_data_masks->lmask |= CD_MASK_PROP_FLOAT2; } } } bool BKE_object_data_transfer_get_dttypes_capacity(const int dtdata_types, bool *r_advanced_mixing, bool *r_threshold) { bool ret = false; *r_advanced_mixing = false; *r_threshold = false; for (int i = 0; (i < DT_TYPE_MAX) && !(ret && *r_advanced_mixing && *r_threshold); i++) { const int dtdata_type = 1 << i; if (!(dtdata_types & dtdata_type)) { continue; } switch (dtdata_type) { /* Vertex data */ case DT_TYPE_MDEFORMVERT: *r_advanced_mixing = true; *r_threshold = true; ret = true; break; case DT_TYPE_SKIN: *r_threshold = true; ret = true; break; case DT_TYPE_BWEIGHT_VERT: ret = true; break; /* Edge data */ case DT_TYPE_SHARP_EDGE: *r_threshold = true; ret = true; break; case DT_TYPE_SEAM: *r_threshold = true; ret = true; break; case DT_TYPE_CREASE: ret = true; break; case DT_TYPE_BWEIGHT_EDGE: ret = true; break; case DT_TYPE_FREESTYLE_EDGE: *r_threshold = true; ret = true; break; /* Loop/Poly data */ case DT_TYPE_UV: ret = true; break; case DT_TYPE_MPROPCOL_VERT: case DT_TYPE_MLOOPCOL_VERT: case DT_TYPE_MPROPCOL_LOOP: case DT_TYPE_MLOOPCOL_LOOP: *r_advanced_mixing = true; *r_threshold = true; ret = true; break; case DT_TYPE_LNOR: *r_advanced_mixing = true; ret = true; break; case DT_TYPE_SHARP_FACE: *r_threshold = true; ret = true; break; case DT_TYPE_FREESTYLE_FACE: *r_threshold = true; ret = true; break; } } return ret; } int BKE_object_data_transfer_get_dttypes_item_types(const int dtdata_types) { int i, ret = 0; for (i = 0; (i < DT_TYPE_MAX) && (ret ^ (ME_VERT | ME_EDGE | ME_LOOP | ME_POLY)); i++) { const int dtdata_type = 1 << i; if (!(dtdata_types & dtdata_type)) { continue; } if (DT_DATATYPE_IS_VERT(dtdata_type)) { ret |= ME_VERT; } if (DT_DATATYPE_IS_EDGE(dtdata_type)) { ret |= ME_EDGE; } if (DT_DATATYPE_IS_LOOP(dtdata_type)) { ret |= ME_LOOP; } if (DT_DATATYPE_IS_FACE(dtdata_type)) { ret |= ME_POLY; } } return ret; } int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type) { switch (dtdata_type) { case DT_TYPE_MDEFORMVERT: return CD_FAKE_MDEFORMVERT; case DT_TYPE_SHAPEKEY: return CD_FAKE_SHAPEKEY; case DT_TYPE_SKIN: return CD_MVERT_SKIN; case DT_TYPE_BWEIGHT_VERT: return CD_FAKE_BWEIGHT; case DT_TYPE_SHARP_EDGE: return CD_FAKE_SHARP; case DT_TYPE_SEAM: return CD_FAKE_SEAM; case DT_TYPE_CREASE: return CD_FAKE_CREASE; case DT_TYPE_BWEIGHT_EDGE: return CD_FAKE_BWEIGHT; case DT_TYPE_FREESTYLE_EDGE: return CD_FREESTYLE_EDGE; case DT_TYPE_UV: return CD_FAKE_UV; case DT_TYPE_SHARP_FACE: return CD_FAKE_SHARP; case DT_TYPE_FREESTYLE_FACE: return CD_FREESTYLE_FACE; case DT_TYPE_LNOR: return CD_FAKE_LNOR; case DT_TYPE_MLOOPCOL_VERT: case DT_TYPE_MLOOPCOL_LOOP: return CD_PROP_BYTE_COLOR; case DT_TYPE_MPROPCOL_VERT: case DT_TYPE_MPROPCOL_LOOP: return CD_PROP_COLOR; default: BLI_assert_unreachable(); } return 0; /* Should never be reached! */ } int BKE_object_data_transfer_dttype_to_srcdst_index(const int dtdata_type) { switch (dtdata_type) { case DT_TYPE_MDEFORMVERT: return DT_MULTILAYER_INDEX_MDEFORMVERT; case DT_TYPE_SHAPEKEY: return DT_MULTILAYER_INDEX_SHAPEKEY; case DT_TYPE_UV: return DT_MULTILAYER_INDEX_UV; case DT_TYPE_MPROPCOL_VERT: case DT_TYPE_MLOOPCOL_VERT: case DT_TYPE_MPROPCOL_VERT | DT_TYPE_MLOOPCOL_VERT: return DT_MULTILAYER_INDEX_VCOL_VERT; case DT_TYPE_MPROPCOL_LOOP: case DT_TYPE_MLOOPCOL_LOOP: case DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP: return DT_MULTILAYER_INDEX_VCOL_LOOP; default: return DT_MULTILAYER_INDEX_INVALID; } } /* ********** */ /** * When transferring color attributes, also transfer the active color attribute string. * If a match can't be found, use the first color layer that can be found (to ensure a valid string * is set). */ static void data_transfer_mesh_attributes_transfer_active_color_string( Mesh *mesh_dst, const Mesh *mesh_src, const AttrDomainMask mask_domain, const int data_type) { if (mesh_dst->active_color_attribute) { return; } const AttributeOwner owner_src = AttributeOwner::from_id(const_cast(&mesh_src->id)); AttributeOwner owner_dst = AttributeOwner::from_id(&mesh_dst->id); const StringRef active_color_src = BKE_id_attributes_active_color_name(&mesh_src->id).value_or(""); if ((data_type == CD_PROP_COLOR) && !BKE_attribute_search( owner_src, active_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { return; } if ((data_type == CD_PROP_BYTE_COLOR) && !BKE_attribute_search( owner_src, active_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { return; } if ((data_type == CD_PROP_COLOR) && BKE_attribute_search( owner_dst, active_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { mesh_dst->active_color_attribute = BLI_strdupn(active_color_src.data(), active_color_src.size()); } else if ((data_type == CD_PROP_BYTE_COLOR) && BKE_attribute_search( owner_dst, active_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { mesh_dst->active_color_attribute = BLI_strdupn(active_color_src.data(), active_color_src.size()); } else { CustomDataLayer *first_color_layer = BKE_attribute_from_index( owner_dst, 0, mask_domain, CD_MASK_COLOR_ALL); if (first_color_layer != nullptr) { mesh_dst->active_color_attribute = BLI_strdup(first_color_layer->name); } } } /** * When transferring color attributes, also transfer the default color attribute string. * If a match cant be found, use the first color layer that can be found (to ensure a valid string * is set). */ static void data_transfer_mesh_attributes_transfer_default_color_string( Mesh *mesh_dst, const Mesh *mesh_src, const AttrDomainMask mask_domain, const int data_type) { if (mesh_dst->default_color_attribute) { return; } const AttributeOwner owner_src = AttributeOwner::from_id(const_cast(&mesh_src->id)); AttributeOwner owner_dst = AttributeOwner::from_id(&mesh_dst->id); const StringRef default_color_src = BKE_id_attributes_default_color_name(&mesh_src->id).value_or(""); if ((data_type == CD_PROP_COLOR) && !BKE_attribute_search( owner_src, default_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { return; } if ((data_type == CD_PROP_BYTE_COLOR) && !BKE_attribute_search( owner_src, default_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { return; } if ((data_type == CD_PROP_COLOR) && BKE_attribute_search( owner_dst, default_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { mesh_dst->default_color_attribute = BLI_strdupn(default_color_src.data(), default_color_src.size()); } else if ((data_type == CD_PROP_BYTE_COLOR) && BKE_attribute_search( owner_dst, default_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { mesh_dst->default_color_attribute = BLI_strdupn(default_color_src.data(), default_color_src.size()); } else { CustomDataLayer *first_color_layer = BKE_attribute_from_index( owner_dst, 0, mask_domain, CD_MASK_COLOR_ALL); if (first_color_layer != nullptr) { mesh_dst->default_color_attribute = BLI_strdup(first_color_layer->name); } } } /* ********** */ static void data_transfer_dtdata_type_postprocess(Mesh *me_dst, const int dtdata_type, const bool changed) { using namespace blender; if (dtdata_type == DT_TYPE_LNOR) { if (!changed) { return; } /* Bake edited destination loop normals into custom normals again. */ CustomData *ldata_dst = &me_dst->corner_data; blender::float3 *loop_nors_dst = static_cast( CustomData_get_layer_for_write(ldata_dst, CD_NORMAL, me_dst->corners_num)); bke::MutableAttributeAccessor attributes = me_dst->attributes_for_write(); bke::SpanAttributeWriter custom_nors_dst = attributes.lookup_or_add_for_write_span( "custom_normal", bke::AttrDomain::Corner); if (!custom_nors_dst) { return; } bke::SpanAttributeWriter sharp_edges = attributes.lookup_or_add_for_write_span( "sharp_edge", bke::AttrDomain::Edge); const VArraySpan sharp_faces = *attributes.lookup("sharp_face", bke::AttrDomain::Face); /* Note loop_nors_dst contains our custom normals as transferred from source... */ blender::bke::mesh::normals_corner_custom_set(me_dst->vert_positions(), me_dst->faces(), me_dst->corner_verts(), me_dst->corner_edges(), me_dst->vert_to_face_map(), me_dst->vert_normals(), me_dst->face_normals_true(), sharp_faces, sharp_edges.span, {loop_nors_dst, me_dst->corners_num}, custom_nors_dst.span); custom_nors_dst.finish(); sharp_edges.finish(); CustomData_free_layers(ldata_dst, CD_NORMAL); } } /* ********** */ static MeshRemapIslandsCalc data_transfer_get_loop_islands_generator(const int cddata_type) { switch (cddata_type) { case CD_FAKE_UV: return BKE_mesh_calc_islands_loop_face_edgeseam; default: break; } return nullptr; } float data_transfer_interp_float_do(const int mix_mode, const float val_dst, const float val_src, const float mix_factor) { float val_ret; if ((mix_mode == CDT_MIX_REPLACE_ABOVE_THRESHOLD && (val_dst < mix_factor)) || (mix_mode == CDT_MIX_REPLACE_BELOW_THRESHOLD && (val_dst > mix_factor))) { return val_dst; /* Do not affect destination. */ } switch (mix_mode) { case CDT_MIX_REPLACE_ABOVE_THRESHOLD: case CDT_MIX_REPLACE_BELOW_THRESHOLD: return val_src; case CDT_MIX_MIX: val_ret = (val_dst + val_src) * 0.5f; break; case CDT_MIX_ADD: val_ret = val_dst + val_src; break; case CDT_MIX_SUB: val_ret = val_dst - val_src; break; case CDT_MIX_MUL: val_ret = val_dst * val_src; break; case CDT_MIX_TRANSFER: default: val_ret = val_src; break; } return interpf(val_ret, val_dst, mix_factor); } /* Helpers to match sources and destinations data layers * (also handles 'conversions' in CD_FAKE cases). */ void data_transfer_layersmapping_add_item(ListBase *r_map, const int cddata_type, const int mix_mode, const float mix_factor, const float *mix_weights, const void *data_src, void *data_dst, const int data_src_n, const int data_dst_n, const size_t elem_size, const size_t data_size, const size_t data_offset, const uint64_t data_flag, cd_datatransfer_interp interp, void *interp_data) { CustomDataTransferLayerMap *item = MEM_callocN(__func__); BLI_assert(data_dst != nullptr); item->data_type = eCustomDataType(cddata_type); item->mix_mode = mix_mode; item->mix_factor = mix_factor; item->mix_weights = mix_weights; item->data_src = data_src; item->data_dst = data_dst; item->data_src_n = data_src_n; item->data_dst_n = data_dst_n; item->elem_size = elem_size; item->data_size = data_size; item->data_offset = data_offset; item->data_flag = data_flag; item->interp = interp; item->interp_data = interp_data; BLI_addtail(r_map, item); } static void data_transfer_layersmapping_add_item_cd(ListBase *r_map, const int cddata_type, const int mix_mode, const float mix_factor, const float *mix_weights, const void *data_src, void *data_dst, cd_datatransfer_interp interp, void *interp_data) { uint64_t data_flag = 0; if (cddata_type == CD_FREESTYLE_EDGE) { data_flag = FREESTYLE_EDGE_MARK; } else if (cddata_type == CD_FREESTYLE_FACE) { data_flag = FREESTYLE_FACE_MARK; } data_transfer_layersmapping_add_item(r_map, cddata_type, mix_mode, mix_factor, mix_weights, data_src, data_dst, 0, 0, 0, 0, 0, data_flag, interp, interp_data); } /** * \note * All those layer mapping handlers return false *only* if they were given invalid parameters. * This means that even if they do nothing, they will return true if all given parameters were OK. * Also, r_map may be nullptr, in which case they will 'only' create/delete destination layers * according to given parameters. */ static bool data_transfer_layersmapping_cdlayers_multisrc_to_dst(ListBase *r_map, const eCustomDataType cddata_type, const int mix_mode, const float mix_factor, const float *mix_weights, const int num_elem_dst, const bool use_create, const bool use_delete, const CustomData *cd_src, CustomData *cd_dst, const int tolayers, const bool *use_layers_src, const int num_layers_src, cd_datatransfer_interp interp, void *interp_data) { const void *data_src; void *data_dst = nullptr; int idx_src = num_layers_src; int idx_dst, tot_dst = CustomData_number_of_layers(cd_dst, cddata_type); bool *data_dst_to_delete = nullptr; if (!use_layers_src) { /* No source at all, we can only delete all destination if requested. */ if (use_delete) { idx_dst = tot_dst; while (idx_dst--) { CustomData_free_layer(cd_dst, cddata_type, idx_dst); } } return true; } switch (tolayers) { case DT_LAYERS_INDEX_DST: idx_dst = tot_dst; /* Find last source actually used! */ while (idx_src-- && !use_layers_src[idx_src]) { /* pass */ } idx_src++; if (idx_dst < idx_src) { if (use_create) { /* Create as much data layers as necessary! */ for (; idx_dst < idx_src; idx_dst++) { CustomData_add_layer( cd_dst, eCustomDataType(cddata_type), CD_SET_DEFAULT, num_elem_dst); } } else { /* Otherwise, just try to map what we can with existing dst data layers. */ idx_src = idx_dst; } } else if (use_delete && idx_dst > idx_src) { while (idx_dst-- > idx_src) { CustomData_free_layer(cd_dst, cddata_type, idx_dst); } } if (r_map) { while (idx_src--) { if (!use_layers_src[idx_src]) { continue; } data_src = CustomData_get_layer_n(cd_src, cddata_type, idx_src); data_dst = CustomData_get_layer_n_for_write(cd_dst, cddata_type, idx_src, num_elem_dst); data_transfer_layersmapping_add_item_cd(r_map, cddata_type, mix_mode, mix_factor, mix_weights, data_src, data_dst, interp, interp_data); } } break; case DT_LAYERS_NAME_DST: if (use_delete) { if (tot_dst) { data_dst_to_delete = MEM_malloc_arrayN(size_t(tot_dst), __func__); memset(data_dst_to_delete, true, sizeof(*data_dst_to_delete) * size_t(tot_dst)); } } while (idx_src--) { const char *name; if (!use_layers_src[idx_src]) { continue; } name = CustomData_get_layer_name(cd_src, cddata_type, idx_src); data_src = CustomData_get_layer_n(cd_src, cddata_type, idx_src); idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name); if (idx_dst == -1) { if (use_create) { CustomData_add_layer_named( cd_dst, eCustomDataType(cddata_type), CD_SET_DEFAULT, num_elem_dst, name); idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name); } else { /* If we are not allowed to create missing dst data layers, * just skip matching src one. */ continue; } } else if (data_dst_to_delete) { data_dst_to_delete[idx_dst] = false; } if (r_map) { data_dst = CustomData_get_layer_n_for_write(cd_dst, cddata_type, idx_dst, num_elem_dst); data_transfer_layersmapping_add_item_cd(r_map, cddata_type, mix_mode, mix_factor, mix_weights, data_src, data_dst, interp, interp_data); } } if (data_dst_to_delete) { /* NOTE: * This won't affect newly created layers, if any, since tot_dst has not been updated! * Also, looping backward ensures us we do not suffer * from index shifting when deleting a layer. */ for (idx_dst = tot_dst; idx_dst--;) { if (data_dst_to_delete[idx_dst]) { CustomData_free_layer(cd_dst, cddata_type, idx_dst); } } MEM_freeN(data_dst_to_delete); } break; default: return false; } return true; } static bool data_transfer_layersmapping_cdlayers(ListBase *r_map, const eCustomDataType cddata_type, const int mix_mode, const float mix_factor, const float *mix_weights, const int num_elem_dst, const bool use_create, const bool use_delete, const CustomData *cd_src, CustomData *cd_dst, const int fromlayers, const int tolayers, cd_datatransfer_interp interp, void *interp_data) { void *data_dst = nullptr; if (CustomData_layertype_is_singleton(cddata_type)) { const void *data_src = CustomData_get_layer(cd_src, cddata_type); if (!data_src) { if (use_delete) { CustomData_free_layer(cd_dst, cddata_type, 0); } return true; } data_dst = CustomData_get_layer_for_write(cd_dst, cddata_type, num_elem_dst); if (!data_dst) { if (!use_create) { return true; } data_dst = CustomData_add_layer( cd_dst, eCustomDataType(cddata_type), CD_SET_DEFAULT, num_elem_dst); } if (r_map) { data_transfer_layersmapping_add_item_cd(r_map, cddata_type, mix_mode, mix_factor, mix_weights, data_src, data_dst, interp, interp_data); } } else if (fromlayers == DT_LAYERS_ACTIVE_SRC || fromlayers >= 0) { /* NOTE: use_delete has not much meaning in this case, ignored. */ int idx_src; if (fromlayers >= 0) { /* Real-layer index */ idx_src = fromlayers; } else { idx_src = CustomData_get_active_layer(cd_src, cddata_type); if (idx_src == -1) { return true; } } const void *data_src = CustomData_get_layer_n(cd_src, cddata_type, idx_src); if (!data_src) { return true; } int idx_dst; if (tolayers >= 0) { /* Real-layer index */ idx_dst = tolayers; data_dst = CustomData_get_layer_n_for_write(cd_dst, cddata_type, idx_dst, num_elem_dst); } else if (tolayers == DT_LAYERS_ACTIVE_DST) { idx_dst = CustomData_get_active_layer(cd_dst, cddata_type); if (idx_dst == -1) { if (!use_create) { return true; } data_dst = CustomData_add_layer( cd_dst, eCustomDataType(cddata_type), CD_SET_DEFAULT, num_elem_dst); } else { data_dst = CustomData_get_layer_n_for_write(cd_dst, cddata_type, idx_dst, num_elem_dst); } } else if (tolayers == DT_LAYERS_INDEX_DST) { int num = CustomData_number_of_layers(cd_dst, cddata_type); idx_dst = idx_src; if (num <= idx_dst) { if (!use_create) { return true; } /* Create as much data layers as necessary! */ for (; num <= idx_dst; num++) { CustomData_add_layer(cd_dst, eCustomDataType(cddata_type), CD_SET_DEFAULT, num_elem_dst); } } data_dst = CustomData_get_layer_n_for_write(cd_dst, cddata_type, idx_dst, num_elem_dst); } else if (tolayers == DT_LAYERS_NAME_DST) { const char *name = CustomData_get_layer_name(cd_src, cddata_type, idx_src); idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name); if (idx_dst == -1) { if (!use_create) { return true; } CustomData_add_layer_named( cd_dst, eCustomDataType(cddata_type), CD_SET_DEFAULT, num_elem_dst, name); idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name); } data_dst = CustomData_get_layer_n_for_write(cd_dst, cddata_type, idx_dst, num_elem_dst); } else { return false; } if (!data_dst) { return false; } if (r_map) { data_transfer_layersmapping_add_item_cd(r_map, cddata_type, mix_mode, mix_factor, mix_weights, data_src, data_dst, interp, interp_data); } } else if (fromlayers == DT_LAYERS_ALL_SRC) { int num_src = CustomData_number_of_layers(cd_src, eCustomDataType(cddata_type)); bool *use_layers_src = num_src ? MEM_malloc_arrayN(size_t(num_src), __func__) : nullptr; bool ret; if (use_layers_src) { memset(use_layers_src, true, sizeof(*use_layers_src) * num_src); } ret = data_transfer_layersmapping_cdlayers_multisrc_to_dst(r_map, cddata_type, mix_mode, mix_factor, mix_weights, num_elem_dst, use_create, use_delete, cd_src, cd_dst, tolayers, use_layers_src, num_src, interp, interp_data); if (use_layers_src) { MEM_freeN(use_layers_src); } return ret; } else { return false; } return true; } static bool data_transfer_layersmapping_generate(ListBase *r_map, Object *ob_src, Object *ob_dst, const Mesh *me_src, Mesh *me_dst, const int elem_type, int cddata_type, int mix_mode, float mix_factor, const float *mix_weights, const int num_elem_dst, const bool use_create, const bool use_delete, const int fromlayers, const int tolayers, SpaceTransform *space_transform) { using namespace blender; const CustomData *cd_src; CustomData *cd_dst; cd_datatransfer_interp interp = nullptr; void *interp_data = nullptr; if (elem_type == ME_VERT) { if (!(cddata_type & CD_FAKE)) { cd_src = &me_src->vert_data; cd_dst = &me_dst->vert_data; if (!data_transfer_layersmapping_cdlayers(r_map, eCustomDataType(cddata_type), mix_mode, mix_factor, mix_weights, num_elem_dst, use_create, use_delete, cd_src, cd_dst, fromlayers, tolayers, interp, interp_data)) { /* We handle specific source selection cases here. */ return false; } return true; } if (cddata_type == CD_FAKE_MDEFORMVERT) { bool ret; cd_src = &me_src->vert_data; cd_dst = &me_dst->vert_data; ret = data_transfer_layersmapping_vgroups(r_map, mix_mode, mix_factor, mix_weights, num_elem_dst, use_create, use_delete, ob_src, ob_dst, cd_src, cd_dst, me_dst != ob_dst->data, fromlayers, tolayers); return ret; } if (cddata_type == CD_FAKE_SHAPEKEY) { /* TODO: leaving shape-keys aside for now, quite specific case, * since we can't access them from mesh vertices :/ */ return false; } if (r_map && cddata_type == CD_FAKE_BWEIGHT) { if (!CustomData_get_layer_named(&me_dst->vert_data, CD_PROP_FLOAT, "bevel_weight_vert")) { CustomData_add_layer_named(&me_dst->vert_data, CD_PROP_FLOAT, CD_SET_DEFAULT, me_dst->verts_num, "bevel_weight_vert"); } data_transfer_layersmapping_add_item_cd( r_map, CD_PROP_FLOAT, mix_mode, mix_factor, mix_weights, CustomData_get_layer_named(&me_src->vert_data, CD_PROP_FLOAT, "bevel_weight_vert"), CustomData_get_layer_named_for_write( &me_dst->vert_data, CD_PROP_FLOAT, "bevel_weight_vert", me_dst->verts_num), interp, interp_data); return true; } } else if (elem_type == ME_EDGE) { if (!(cddata_type & CD_FAKE)) { /* Unused for edges, currently... */ cd_src = &me_src->edge_data; cd_dst = &me_dst->edge_data; if (!data_transfer_layersmapping_cdlayers(r_map, eCustomDataType(cddata_type), mix_mode, mix_factor, mix_weights, num_elem_dst, use_create, use_delete, cd_src, cd_dst, fromlayers, tolayers, interp, interp_data)) { /* We handle specific source selection cases here. */ return false; } return true; } if (r_map && cddata_type == CD_FAKE_SEAM) { if (!CustomData_has_layer_named(&me_dst->edge_data, CD_PROP_BOOL, "uv_seam")) { CustomData_add_layer_named( &me_dst->edge_data, CD_PROP_BOOL, CD_SET_DEFAULT, me_dst->edges_num, "uv_seam"); } data_transfer_layersmapping_add_item_cd( r_map, CD_PROP_BOOL, mix_mode, mix_factor, mix_weights, CustomData_get_layer_named(&me_src->edge_data, CD_PROP_BOOL, "uv_seam"), CustomData_get_layer_named_for_write( &me_dst->edge_data, CD_PROP_BOOL, "uv_seam", me_dst->edges_num), interp, interp_data); return true; } if (r_map && cddata_type == CD_FAKE_SHARP) { if (!CustomData_has_layer_named(&me_dst->edge_data, CD_PROP_BOOL, "sharp_edge")) { CustomData_add_layer_named( &me_dst->edge_data, CD_PROP_BOOL, CD_SET_DEFAULT, me_dst->edges_num, "sharp_edge"); } data_transfer_layersmapping_add_item_cd( r_map, CD_PROP_BOOL, mix_mode, mix_factor, mix_weights, CustomData_get_layer_named(&me_src->edge_data, CD_PROP_BOOL, "sharp_edge"), CustomData_get_layer_named_for_write( &me_dst->edge_data, CD_PROP_BOOL, "sharp_edge", me_dst->edges_num), interp, interp_data); return true; } if (r_map && cddata_type == CD_FAKE_BWEIGHT) { if (!CustomData_get_layer_named(&me_dst->edge_data, CD_PROP_FLOAT, "bevel_weight_edge")) { CustomData_add_layer_named(&me_dst->edge_data, CD_PROP_FLOAT, CD_SET_DEFAULT, me_dst->edges_num, "bevel_weight_edge"); } data_transfer_layersmapping_add_item_cd( r_map, CD_PROP_FLOAT, mix_mode, mix_factor, mix_weights, CustomData_get_layer_named(&me_src->edge_data, CD_PROP_FLOAT, "bevel_weight_edge"), CustomData_get_layer_named_for_write( &me_dst->edge_data, CD_PROP_FLOAT, "bevel_weight_edge", me_dst->edges_num), interp, interp_data); return true; } if (r_map && cddata_type == CD_FAKE_CREASE) { if (!CustomData_get_layer_named(&me_dst->edge_data, CD_PROP_FLOAT, "crease_edge")) { CustomData_add_layer_named( &me_dst->edge_data, CD_PROP_FLOAT, CD_SET_DEFAULT, me_dst->edges_num, "crease_edge"); } data_transfer_layersmapping_add_item_cd( r_map, CD_PROP_FLOAT, mix_mode, mix_factor, mix_weights, CustomData_get_layer_named(&me_src->edge_data, CD_PROP_FLOAT, "crease_edge"), CustomData_get_layer_named_for_write( &me_dst->edge_data, CD_PROP_FLOAT, "crease_edge", me_dst->edges_num), interp, interp_data); return true; } return false; } else if (elem_type == ME_LOOP) { if (cddata_type == CD_FAKE_UV) { cddata_type = CD_PROP_FLOAT2; } else if (cddata_type == CD_FAKE_LNOR) { if (r_map) { /* Use #CD_NORMAL as a temporary storage for custom normals in 3D vector form. * A post-process step will convert this layer to "custom_normal". */ float3 *dst_data = static_cast( CustomData_get_layer_for_write(&me_dst->corner_data, CD_NORMAL, me_dst->corners_num)); if (!dst_data) { dst_data = static_cast(CustomData_add_layer( &me_dst->corner_data, CD_NORMAL, CD_SET_DEFAULT, me_dst->corners_num)); } if (mix_factor != 1.0f || mix_weights) { MutableSpan(dst_data, me_dst->corners_num).copy_from(me_dst->corner_normals()); } /* Post-process will convert it back to "custom_normal". */ data_transfer_layersmapping_add_item_cd(r_map, CD_NORMAL, mix_mode, mix_factor, mix_weights, me_src->corner_normals().data(), dst_data, customdata_data_transfer_interp_normal_normals, space_transform); } return true; } if (!(cddata_type & CD_FAKE)) { cd_src = &me_src->corner_data; cd_dst = &me_dst->corner_data; if (!data_transfer_layersmapping_cdlayers(r_map, eCustomDataType(cddata_type), mix_mode, mix_factor, mix_weights, num_elem_dst, use_create, use_delete, cd_src, cd_dst, fromlayers, tolayers, interp, interp_data)) { /* We handle specific source selection cases here. */ return false; } return true; } return false; } else if (elem_type == ME_POLY) { if (cddata_type == CD_FAKE_UV) { cddata_type = CD_PROP_FLOAT2; } if (!(cddata_type & CD_FAKE)) { cd_src = &me_src->face_data; cd_dst = &me_dst->face_data; if (!data_transfer_layersmapping_cdlayers(r_map, eCustomDataType(cddata_type), mix_mode, mix_factor, mix_weights, num_elem_dst, use_create, use_delete, cd_src, cd_dst, fromlayers, tolayers, interp, interp_data)) { /* We handle specific source selection cases here. */ return false; } return true; } if (r_map && cddata_type == CD_FAKE_SHARP) { if (!CustomData_has_layer_named(&me_dst->face_data, CD_PROP_BOOL, "sharp_face")) { CustomData_add_layer_named( &me_dst->face_data, CD_PROP_BOOL, CD_SET_DEFAULT, me_dst->faces_num, "sharp_face"); } data_transfer_layersmapping_add_item_cd( r_map, CD_PROP_BOOL, mix_mode, mix_factor, mix_weights, CustomData_get_layer_named(&me_src->face_data, CD_PROP_BOOL, "sharp_face"), CustomData_get_layer_named_for_write( &me_dst->face_data, CD_PROP_BOOL, "sharp_face", num_elem_dst), interp, interp_data); return true; } return false; } return false; } void BKE_object_data_transfer_layout(Depsgraph *depsgraph, Object *ob_src, Object *ob_dst, const int data_types, const bool use_delete, const int fromlayers_select[DT_MULTILAYER_INDEX_MAX], const int tolayers_select[DT_MULTILAYER_INDEX_MAX]) { Mesh *me_dst; const bool use_create = true; /* We always create needed layers here. */ BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH)); me_dst = static_cast(ob_dst->data); /* Get source evaluated mesh. */ const Object *ob_src_eval = DEG_get_evaluated(depsgraph, ob_src); const Mesh *me_src = BKE_object_get_evaluated_mesh(ob_src_eval); if (!me_src) { return; } /* Check all possible data types. */ for (int i = 0; i < DT_TYPE_MAX; i++) { const int dtdata_type = 1 << i; int cddata_type; int fromlayers, tolayers, fromto_idx; if (!(data_types & dtdata_type)) { continue; } cddata_type = BKE_object_data_transfer_dttype_to_cdtype(dtdata_type); fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(dtdata_type); if (fromto_idx != DT_MULTILAYER_INDEX_INVALID) { fromlayers = fromlayers_select[fromto_idx]; tolayers = tolayers_select[fromto_idx]; } else { fromlayers = tolayers = 0; } if (DT_DATATYPE_IS_VERT(dtdata_type)) { const int num_elem_dst = me_dst->verts_num; data_transfer_layersmapping_generate(nullptr, ob_src, ob_dst, me_src, me_dst, ME_VERT, cddata_type, 0, 0.0f, nullptr, num_elem_dst, use_create, use_delete, fromlayers, tolayers, nullptr); /* Make sure we have active/default color layers if none existed before. * Use the active/default from src (if it was transferred), otherwise the first. */ if (ELEM(cddata_type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)) { data_transfer_mesh_attributes_transfer_active_color_string( me_dst, me_src, ATTR_DOMAIN_MASK_POINT, cddata_type); data_transfer_mesh_attributes_transfer_default_color_string( me_dst, me_src, ATTR_DOMAIN_MASK_POINT, cddata_type); } } if (DT_DATATYPE_IS_EDGE(dtdata_type)) { const int num_elem_dst = me_dst->edges_num; data_transfer_layersmapping_generate(nullptr, ob_src, ob_dst, me_src, me_dst, ME_EDGE, cddata_type, 0, 0.0f, nullptr, num_elem_dst, use_create, use_delete, fromlayers, tolayers, nullptr); } if (DT_DATATYPE_IS_LOOP(dtdata_type)) { const int num_elem_dst = me_dst->corners_num; data_transfer_layersmapping_generate(nullptr, ob_src, ob_dst, me_src, me_dst, ME_LOOP, cddata_type, 0, 0.0f, nullptr, num_elem_dst, use_create, use_delete, fromlayers, tolayers, nullptr); /* Make sure we have active/default color layers if none existed before. * Use the active/default from src (if it was transferred), otherwise the first. */ if (ELEM(cddata_type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)) { data_transfer_mesh_attributes_transfer_active_color_string( me_dst, me_src, ATTR_DOMAIN_MASK_CORNER, cddata_type); data_transfer_mesh_attributes_transfer_default_color_string( me_dst, me_src, ATTR_DOMAIN_MASK_CORNER, cddata_type); } } if (DT_DATATYPE_IS_FACE(dtdata_type)) { const int num_elem_dst = me_dst->faces_num; data_transfer_layersmapping_generate(nullptr, ob_src, ob_dst, me_src, me_dst, ME_POLY, cddata_type, 0, 0.0f, nullptr, num_elem_dst, use_create, use_delete, fromlayers, tolayers, nullptr); } } } bool BKE_object_data_transfer_ex(Depsgraph *depsgraph, Object *ob_src, Object *ob_dst, Mesh *me_dst, const int data_types, bool use_create, const int map_vert_mode, const int map_edge_mode, const int map_loop_mode, const int map_face_mode, SpaceTransform *space_transform, const bool auto_transform, const float max_distance, const float ray_radius, const float islands_handling_precision, const int fromlayers_select[DT_MULTILAYER_INDEX_MAX], const int tolayers_select[DT_MULTILAYER_INDEX_MAX], const int mix_mode, const float mix_factor, const char *vgroup_name, const bool invert_vgroup, ReportList *reports) { #define VDATA 0 #define EDATA 1 #define LDATA 2 #define PDATA 3 #define DATAMAX 4 SpaceTransform auto_space_transform; const Mesh *me_src; const MDeformVert *mdef = nullptr; int vg_idx = -1; float *weights[DATAMAX] = {nullptr}; MeshPairRemap geom_map[DATAMAX] = {{0}}; bool geom_map_init[DATAMAX] = {false}; ListBase lay_map = {nullptr}; bool changed = false; bool is_modifier = false; const bool use_delete = false; /* We never delete data layers from destination here. */ BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH)); if (me_dst) { /* Never create needed custom layers on passed destination mesh * (assumed to *not* be ob_dst->data, aka modifier case). */ use_create = false; is_modifier = true; } else { me_dst = static_cast(ob_dst->data); } if (vgroup_name) { mdef = static_cast( CustomData_get_layer(&me_dst->vert_data, CD_MDEFORMVERT)); if (mdef) { vg_idx = BKE_id_defgroup_name_index(&me_dst->id, vgroup_name); } } /* Get source evaluated mesh. */ if (is_modifier) { me_src = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_src); } else { const Object *ob_eval = DEG_get_evaluated(depsgraph, ob_src); me_src = BKE_object_get_evaluated_mesh(ob_eval); } if (!me_src) { return changed; } BKE_mesh_wrapper_ensure_mdata(const_cast(me_src)); if (auto_transform) { if (space_transform == nullptr) { space_transform = &auto_space_transform; } BKE_mesh_remap_find_best_match_from_mesh( reinterpret_cast(me_dst->vert_positions().data()), me_dst->verts_num, me_src, space_transform); } /* Check all possible data types. * Note item mappings and destination mix weights are cached. */ for (int i = 0; i < DT_TYPE_MAX; i++) { const int dtdata_type = 1 << i; int cddata_type; int fromlayers, tolayers, fromto_idx; if (!(data_types & dtdata_type)) { continue; } cddata_type = BKE_object_data_transfer_dttype_to_cdtype(dtdata_type); fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(dtdata_type); if (fromto_idx != DT_MULTILAYER_INDEX_INVALID) { fromlayers = fromlayers_select[fromto_idx]; tolayers = tolayers_select[fromto_idx]; } else { fromlayers = tolayers = 0; } if (DT_DATATYPE_IS_VERT(dtdata_type)) { blender::MutableSpan positions_dst = me_dst->vert_positions_for_write(); const int num_verts_dst = me_dst->verts_num; if (!geom_map_init[VDATA]) { const int num_verts_src = me_src->verts_num; if ((map_vert_mode == MREMAP_MODE_TOPOLOGY) && (num_verts_dst != num_verts_src)) { BKE_report(reports, RPT_ERROR, "Source and destination meshes do not have the same number of vertices, " "'Topology' mapping cannot be used in this case"); continue; } if ((map_vert_mode & MREMAP_USE_EDGE) && (me_src->edges_num == 0)) { BKE_report(reports, RPT_ERROR, "Source mesh does not have any edges, " "none of the 'Edge' mappings can be used in this case"); continue; } if ((map_vert_mode & MREMAP_USE_POLY) && (me_src->faces_num == 0)) { BKE_report(reports, RPT_ERROR, "Source mesh does not have any faces, " "none of the 'Face' mappings can be used in this case"); continue; } if (ELEM(0, num_verts_dst, num_verts_src)) { BKE_report(reports, RPT_ERROR, "Source or destination meshes do not have any vertices, cannot transfer " "vertex data"); continue; } BKE_mesh_remap_calc_verts_from_mesh( map_vert_mode, space_transform, max_distance, ray_radius, reinterpret_cast(positions_dst.data()), num_verts_dst, me_src, me_dst, &geom_map[VDATA]); geom_map_init[VDATA] = true; } if (mdef && vg_idx != -1 && !weights[VDATA]) { weights[VDATA] = MEM_malloc_arrayN(size_t(num_verts_dst), __func__); BKE_defvert_extract_vgroup_to_vertweights( mdef, vg_idx, num_verts_dst, invert_vgroup, weights[VDATA]); } if (data_transfer_layersmapping_generate(&lay_map, ob_src, ob_dst, me_src, me_dst, ME_VERT, cddata_type, mix_mode, mix_factor, weights[VDATA], num_verts_dst, use_create, use_delete, fromlayers, tolayers, space_transform)) { changed |= (lay_map.first != nullptr); LISTBASE_FOREACH (CustomDataTransferLayerMap *, lay_mapit, &lay_map) { CustomData_data_transfer(&geom_map[VDATA], lay_mapit); } BLI_freelistN(&lay_map); } } if (DT_DATATYPE_IS_EDGE(dtdata_type)) { blender::MutableSpan positions_dst = me_dst->vert_positions_for_write(); const int num_verts_dst = me_dst->verts_num; const blender::Span edges_dst = me_dst->edges(); if (!geom_map_init[EDATA]) { const int num_edges_src = me_src->edges_num; if ((map_edge_mode == MREMAP_MODE_TOPOLOGY) && (edges_dst.size() != num_edges_src)) { BKE_report(reports, RPT_ERROR, "Source and destination meshes do not have the same number of edges, " "'Topology' mapping cannot be used in this case"); continue; } if ((map_edge_mode & MREMAP_USE_POLY) && (me_src->faces_num == 0)) { BKE_report(reports, RPT_ERROR, "Source mesh does not have any faces, " "none of the 'Face' mappings can be used in this case"); continue; } if (ELEM(0, edges_dst.size(), num_edges_src)) { BKE_report( reports, RPT_ERROR, "Source or destination meshes do not have any edges, cannot transfer edge data"); continue; } BKE_mesh_remap_calc_edges_from_mesh( map_edge_mode, space_transform, max_distance, ray_radius, reinterpret_cast(positions_dst.data()), num_verts_dst, edges_dst.data(), edges_dst.size(), me_src, me_dst, &geom_map[EDATA]); geom_map_init[EDATA] = true; } if (mdef && vg_idx != -1 && !weights[EDATA]) { weights[EDATA] = MEM_malloc_arrayN(size_t(edges_dst.size()), __func__); BKE_defvert_extract_vgroup_to_edgeweights(mdef, vg_idx, num_verts_dst, edges_dst.data(), edges_dst.size(), invert_vgroup, weights[EDATA]); } if (data_transfer_layersmapping_generate(&lay_map, ob_src, ob_dst, me_src, me_dst, ME_EDGE, cddata_type, mix_mode, mix_factor, weights[EDATA], edges_dst.size(), use_create, use_delete, fromlayers, tolayers, space_transform)) { changed |= (lay_map.first != nullptr); LISTBASE_FOREACH (CustomDataTransferLayerMap *, lay_mapit, &lay_map) { CustomData_data_transfer(&geom_map[EDATA], lay_mapit); } BLI_freelistN(&lay_map); } } if (DT_DATATYPE_IS_LOOP(dtdata_type)) { const blender::Span positions_dst = me_dst->vert_positions(); const int num_verts_dst = me_dst->verts_num; const blender::OffsetIndices faces_dst = me_dst->faces(); const blender::Span corner_verts_dst = me_dst->corner_verts(); MeshRemapIslandsCalc island_callback = data_transfer_get_loop_islands_generator(cddata_type); if (!geom_map_init[LDATA]) { const int num_loops_src = me_src->corners_num; if ((map_loop_mode == MREMAP_MODE_TOPOLOGY) && (corner_verts_dst.size() != num_loops_src)) { BKE_report(reports, RPT_ERROR, "Source and destination meshes do not have the same number of face corners, " "'Topology' mapping cannot be used in this case"); continue; } if ((map_loop_mode & MREMAP_USE_EDGE) && (me_src->edges_num == 0)) { BKE_report(reports, RPT_ERROR, "Source mesh does not have any edges, " "none of the 'Edge' mappings can be used in this case"); continue; } if (ELEM(0, corner_verts_dst.size(), num_loops_src)) { BKE_report( reports, RPT_ERROR, "Source or destination meshes do not have any faces, cannot transfer corner data"); continue; } BKE_mesh_remap_calc_loops_from_mesh( map_loop_mode, space_transform, max_distance, ray_radius, me_dst, reinterpret_cast(positions_dst.data()), num_verts_dst, corner_verts_dst.data(), corner_verts_dst.size(), faces_dst, me_src, island_callback, islands_handling_precision, &geom_map[LDATA]); geom_map_init[LDATA] = true; } if (mdef && vg_idx != -1 && !weights[LDATA]) { weights[LDATA] = MEM_malloc_arrayN(size_t(corner_verts_dst.size()), __func__); BKE_defvert_extract_vgroup_to_loopweights(mdef, vg_idx, num_verts_dst, corner_verts_dst.data(), corner_verts_dst.size(), invert_vgroup, weights[LDATA]); } if (data_transfer_layersmapping_generate(&lay_map, ob_src, ob_dst, me_src, me_dst, ME_LOOP, cddata_type, mix_mode, mix_factor, weights[LDATA], corner_verts_dst.size(), use_create, use_delete, fromlayers, tolayers, space_transform)) { changed |= (lay_map.first != nullptr); LISTBASE_FOREACH (CustomDataTransferLayerMap *, lay_mapit, &lay_map) { CustomData_data_transfer(&geom_map[LDATA], lay_mapit); } BLI_freelistN(&lay_map); } } if (DT_DATATYPE_IS_FACE(dtdata_type)) { const blender::Span positions_dst = me_dst->vert_positions(); const int num_verts_dst = me_dst->verts_num; const blender::OffsetIndices faces_dst = me_dst->faces(); const blender::Span corner_verts_dst = me_dst->corner_verts(); if (!geom_map_init[PDATA]) { const int num_faces_src = me_src->faces_num; if ((map_face_mode == MREMAP_MODE_TOPOLOGY) && (faces_dst.size() != num_faces_src)) { BKE_report(reports, RPT_ERROR, "Source and destination meshes do not have the same number of faces, " "'Topology' mapping cannot be used in this case"); continue; } if ((map_face_mode & MREMAP_USE_EDGE) && (me_src->edges_num == 0)) { BKE_report(reports, RPT_ERROR, "Source mesh does not have any edges, " "none of the 'Edge' mappings can be used in this case"); continue; } if (ELEM(0, faces_dst.size(), num_faces_src)) { BKE_report( reports, RPT_ERROR, "Source or destination meshes do not have any faces, cannot transfer face data"); continue; } BKE_mesh_remap_calc_faces_from_mesh( map_face_mode, space_transform, max_distance, ray_radius, me_dst, reinterpret_cast(positions_dst.data()), num_verts_dst, corner_verts_dst.data(), faces_dst, me_src, &geom_map[PDATA]); geom_map_init[PDATA] = true; } if (mdef && vg_idx != -1 && !weights[PDATA]) { weights[PDATA] = MEM_malloc_arrayN(size_t(faces_dst.size()), __func__); BKE_defvert_extract_vgroup_to_faceweights(mdef, vg_idx, num_verts_dst, corner_verts_dst.data(), corner_verts_dst.size(), faces_dst, invert_vgroup, weights[PDATA]); } if (data_transfer_layersmapping_generate(&lay_map, ob_src, ob_dst, me_src, me_dst, ME_POLY, cddata_type, mix_mode, mix_factor, weights[PDATA], faces_dst.size(), use_create, use_delete, fromlayers, tolayers, space_transform)) { changed |= (lay_map.first != nullptr); LISTBASE_FOREACH (CustomDataTransferLayerMap *, lay_mapit, &lay_map) { CustomData_data_transfer(&geom_map[PDATA], lay_mapit); } BLI_freelistN(&lay_map); } } data_transfer_dtdata_type_postprocess(me_dst, dtdata_type, changed); } for (int i = 0; i < DATAMAX; i++) { BKE_mesh_remap_free(&geom_map[i]); MEM_SAFE_FREE(weights[i]); } return changed; #undef VDATA #undef EDATA #undef LDATA #undef PDATA #undef DATAMAX } bool BKE_object_data_transfer_mesh(Depsgraph *depsgraph, Object *ob_src, Object *ob_dst, const int data_types, const bool use_create, const int map_vert_mode, const int map_edge_mode, const int map_loop_mode, const int map_face_mode, SpaceTransform *space_transform, const bool auto_transform, const float max_distance, const float ray_radius, const float islands_handling_precision, const int fromlayers_select[DT_MULTILAYER_INDEX_MAX], const int tolayers_select[DT_MULTILAYER_INDEX_MAX], const int mix_mode, const float mix_factor, const char *vgroup_name, const bool invert_vgroup, ReportList *reports) { return BKE_object_data_transfer_ex(depsgraph, ob_src, ob_dst, nullptr, data_types, use_create, map_vert_mode, map_edge_mode, map_loop_mode, map_face_mode, space_transform, auto_transform, max_distance, ray_radius, islands_handling_precision, fromlayers_select, tolayers_select, mix_mode, mix_factor, vgroup_name, invert_vgroup, reports); }