diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d.py | 2 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_customdata.h | 88 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_deform.h | 17 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_object_data_transfer.h | 158 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_object_deform.h | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/customdata.c | 331 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/data_transfer_intern.h | 60 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/deform.c | 391 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object_data_transfer.c | 1227 | ||||
-rw-r--r-- | source/blender/editors/object/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_data_transfer.c | 620 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 4 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 7 | ||||
-rw-r--r-- | source/blender/makesrna/RNA_enum_types.h | 8 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_modifier.c | 108 |
17 files changed, 3023 insertions, 16 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 6120e425fd2..6bd2c4cd9b3 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1095,6 +1095,8 @@ class VIEW3D_MT_object(Menu): layout.separator() layout.operator("object.join") + layout.operator("object.data_transfer") + layout.operator("object.datalayout_transfer") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index b7fe9c69c1f..7aa80bcd791 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -100,6 +100,13 @@ class VIEW3D_PT_tools_object(View3DPanel, Panel): row.operator("object.shade_smooth", text="Smooth") row.operator("object.shade_flat", text="Flat") + if obj_type in {'MESH'}: + col = layout.column(align=True) + col.label(text="Data Transfer:") + row = col.row(align=True) + row.operator("object.data_transfer", text="Data") + row.operator("object.datalayout_transfer", text="Data Layout") + class VIEW3D_PT_tools_add_object(View3DPanel, Panel): bl_category = "Create" diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 30a58891bda..08edde1cec0 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -41,6 +41,8 @@ extern "C" { #include "BLI_sys_types.h" #include "BLI_utildefines.h" +#include "DNA_customdata_types.h" + struct BMesh; struct ID; struct CustomData; @@ -77,6 +79,9 @@ extern const CustomDataMask CD_MASK_EVERYTHING; void customData_mask_layers__print(CustomDataMask mask); +typedef void (*cd_interp)(void **sources, const float *weights, const float *sub_weights, int count, void *dest); +typedef void (*cd_copy)(const void *source, void *dest, int count); + /** * Checks if the layer at physical offset \a layer_n (in data->layers) support math * the below operations. @@ -96,6 +101,9 @@ bool CustomData_bmesh_has_free(const struct CustomData *data); * implemented for mloopuv/mloopcol, for now.*/ void CustomData_data_copy_value(int type, const void *source, void *dest); +/* Same as above, but doing advanced mixing. Only available for a few types of data (like colors...). */ +void CustomData_data_mix_value(int type, const void *source, void *dest, const int mixmode, const float mixfactor); + /* compares if data1 is equal to data2. type is a valid CustomData type * enum (e.g. CD_MLOOPUV). the layer type's equal function is used to compare * the data, if it exists, otherwise memcmp is used.*/ @@ -246,14 +254,14 @@ void *CustomData_bmesh_get_n(const struct CustomData *data, void *block, int typ void *CustomData_bmesh_get_layer_n(const struct CustomData *data, void *block, int n); bool CustomData_set_layer_name(const struct CustomData *data, int type, int n, const char *name); +const char *CustomData_get_layer_name(const struct CustomData *data, int type, int n); /* gets a pointer to the active or first layer of type * returns NULL if there is no layer of type */ void *CustomData_get_layer(const struct CustomData *data, int type); void *CustomData_get_layer_n(const struct CustomData *data, int type, int n); -void *CustomData_get_layer_named(const struct CustomData *data, int type, - const char *name); +void *CustomData_get_layer_named(const struct CustomData *data, int type, const char *name); int CustomData_get_offset(const struct CustomData *data, int type); int CustomData_get_n_offset(const struct CustomData *data, int type, int n); @@ -362,6 +370,82 @@ void CustomData_external_read(struct CustomData *data, void CustomData_external_reload(struct CustomData *data, struct ID *id, CustomDataMask mask, int totelem); +/* Mesh-to-mesh transfer data. */ + +struct MeshPairRemap; +typedef struct CustomDataTransferLayerMap CustomDataTransferLayerMap; + +typedef void (*cd_datatransfer_interp)( + const CustomDataTransferLayerMap *laymap, void *dest, + void **sources, const float *weights, const int count, const float mix_factor); + +/** + * Fake CD_LAYERS (those are actually 'real' data stored directly into elements' structs, or otherwise not (directly) + * accessible to usual CDLayer system). */ +enum { + CD_FAKE = 1 << 8, + + /* Vertices. */ + CD_FAKE_MDEFORMVERT = CD_FAKE | CD_MDEFORMVERT, /* *sigh* due to how vgroups are stored :( . */ + CD_FAKE_SHAPEKEY = CD_FAKE | CD_SHAPEKEY, /* Not available as real CD layer in non-bmesh context. */ + + /* Edges. */ + CD_FAKE_SEAM = CD_FAKE | 100, /* UV seam flag for edges. */ + CD_FAKE_CREASE = CD_FAKE | CD_CREASE, /* *sigh*. */ + + /* Multiple types of mesh elements... */ + CD_FAKE_BWEIGHT = CD_FAKE | CD_BWEIGHT, /* *sigh*. */ + CD_FAKE_UV = CD_FAKE | CD_MLOOPUV, /* UV flag, because we handle both loop's UVs and poly's textures. */ + + CD_FAKE_SHARP = CD_FAKE | 200, /* Sharp flag for edges, smooth flag for faces. */ +}; + +enum { + ME_VERT = 1 << 0, + ME_EDGE = 1 << 1, + ME_POLY = 1 << 2, + ME_LOOP = 1 << 3, +}; + +/** + * How to filter out some elements (to leave untouched). + * Note those options are highly dependent on type of transferred data! */ +enum { + CDT_MIX_NOMIX = -1, /* Special case, only used because we abuse 'copy' CD callback. */ + CDT_MIX_TRANSFER = 0, + CDT_MIX_REPLACE_ABOVE_THRESHOLD = 1, + CDT_MIX_REPLACE_BELOW_THRESHOLD = 2, + CDT_MIX_MIX = 16, + CDT_MIX_ADD = 17, + CDT_MIX_SUB = 18, + CDT_MIX_MUL = 19, + /* etc. etc. */ +}; + +typedef struct CustomDataTransferLayerMap { + CustomDataTransferLayerMap *next, *prev; + + int data_type; + int mix_mode; + float mix_factor; + const float *mix_weights; /* If non-NULL, array of weights, one for each dest item, replaces mix_factor. */ + + void *data_src; /* Data source array (can be regular CD data, vertices/edges/etc., keyblocks...). */ + void *data_dst; /* Data dest array (same type as dat_src). */ + int data_src_n; /* Index to affect in data_src (used e.g. for vgroups). */ + int data_dst_n; /* Index to affect in data_dst (used e.g. for vgroups). */ + size_t elem_size; /* Size of one element of data_src/data_dst. */ + + size_t data_size; /* Size of actual data we transfer. */ + size_t data_offset; /* Offset of actual data we transfer (in element contained in data_src/dst). */ + uint64_t data_flag; /* For bitflag transfer, flag(s) to affect in transfered data. */ + + cd_datatransfer_interp interp; +} CustomDataTransferLayerMap; + +/* Those functions assume src_n and dst_n layers of given type exist in resp. src and dst. */ +void CustomData_data_transfer(const struct MeshPairRemap *me_remap, const CustomDataTransferLayerMap *laymap); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index e203549fef5..08312035e40 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -39,6 +39,10 @@ struct Object; struct ListBase; struct bDeformGroup; struct MDeformVert; +struct MVert; +struct MEdge; +struct MLoop; +struct MPoly; struct bDeformGroup *BKE_defgroup_new(struct Object *ob, const char *name); void defgroup_copy_list(struct ListBase *lb1, struct ListBase *lb2); @@ -85,6 +89,19 @@ void defvert_normalize_lock_map(struct MDeformVert *dvert, const bool *vgroup_subset, const int vgroup_tot, const bool *lock_flags, const int defbase_tot); +/* Utilities to 'extract' a given vgroup into a simple float array, for verts, but also edges/polys/loops. */ +void BKE_defvert_extract_vgroup_to_vertweights( + struct MDeformVert *dvert, const int defgroup, const int num_verts, float *r_weights, const bool invert_vgroup); +void BKE_defvert_extract_vgroup_to_edgeweights( + struct MDeformVert *dvert, const int defgroup, const int num_verts, struct MEdge *edges, const int num_edges, + float *r_weights, const bool invert_vgroup); +void BKE_defvert_extract_vgroup_to_loopweights( + struct MDeformVert *dvert, const int defgroup, const int num_verts, struct MLoop *loops, const int num_loops, + float *r_weights, const bool invert_vgroup); +void BKE_defvert_extract_vgroup_to_polyweights( + struct MDeformVert *dvert, const int defgroup, const int num_verts, struct MLoop *loops, const int num_loops, + struct MPoly *polys, const int num_polys, float *r_weights, const bool invert_vgroup); + /* utility function, note that MAX_VGROUP_NAME chars is the maximum string length since its only * used with defgroups currently */ diff --git a/source/blender/blenkernel/BKE_object_data_transfer.h b/source/blender/blenkernel/BKE_object_data_transfer.h new file mode 100644 index 00000000000..fe218901201 --- /dev/null +++ b/source/blender/blenkernel/BKE_object_data_transfer.h @@ -0,0 +1,158 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2014 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Bastien Montagne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/BKE_object_data_transfer.h + * \ingroup bke + */ + +#ifndef __BKE_OBJECT_DATA_TRANSFER_H__ +#define __BKE_OBJECT_DATA_TRANSFER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "BKE_customdata.h" + +struct Object; +struct Scene; +struct SpaceTransform; +struct ReportList; + +/* Warning, those def are stored in files (TransferData modifier), *DO NOT* modify those values. */ +enum { + DT_TYPE_MDEFORMVERT = 1 << 0, + DT_TYPE_SHAPEKEY = 1 << 1, + DT_TYPE_SKIN = 1 << 2, + DT_TYPE_BWEIGHT_VERT = 1 << 3, + + DT_TYPE_SHARP_EDGE = 1 << 8, + DT_TYPE_SEAM = 1 << 9, + DT_TYPE_CREASE = 1 << 10, + DT_TYPE_BWEIGHT_EDGE = 1 << 11, + DT_TYPE_FREESTYLE_EDGE = 1 << 12, + + DT_TYPE_VCOL = 1 << 16, + + DT_TYPE_UV = 1 << 24, + DT_TYPE_SHARP_FACE = 1 << 25, + DT_TYPE_FREESTYLE_FACE = 1 << 26, +#define \ + DT_TYPE_MAX 27 + + DT_TYPE_VERT_ALL = DT_TYPE_MDEFORMVERT | DT_TYPE_SHAPEKEY | DT_TYPE_SKIN | DT_TYPE_BWEIGHT_VERT, + DT_TYPE_EDGE_ALL = DT_TYPE_SHARP_EDGE | DT_TYPE_SEAM | DT_TYPE_CREASE | DT_TYPE_BWEIGHT_EDGE | + DT_TYPE_FREESTYLE_EDGE, + DT_TYPE_LOOP_ALL = DT_TYPE_VCOL | DT_TYPE_UV, + DT_TYPE_POLY_ALL = DT_TYPE_UV | DT_TYPE_SHARP_FACE | DT_TYPE_FREESTYLE_FACE, +}; + + +CustomDataMask BKE_object_data_transfer_dttypes_to_cdmask(const int dtdata_types); +bool BKE_object_data_transfer_get_dttypes_capacity( + const int dtdata_types, bool *r_advanced_mixing, bool *r_threshold); +int BKE_object_data_transfer_get_dttypes_item_types(const int dtdata_types); + +int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type); +int BKE_object_data_transfer_dttype_to_srcdst_index(const int dtdata_type); + +#define DT_DATATYPE_IS_VERT(_dt) \ + ELEM(_dt, DT_TYPE_MDEFORMVERT, DT_TYPE_SHAPEKEY, DT_TYPE_SKIN, DT_TYPE_BWEIGHT_VERT) +#define DT_DATATYPE_IS_EDGE(_dt) \ + ELEM(_dt, DT_TYPE_CREASE, DT_TYPE_SHARP_EDGE, DT_TYPE_SEAM, DT_TYPE_BWEIGHT_EDGE, DT_TYPE_FREESTYLE_EDGE) +#define DT_DATATYPE_IS_LOOP(_dt) \ + ELEM(_dt, DT_TYPE_UV, DT_TYPE_VCOL) +#define DT_DATATYPE_IS_POLY(_dt) \ + ELEM(_dt, DT_TYPE_UV, DT_TYPE_SHARP_FACE, DT_TYPE_FREESTYLE_FACE) + +#define DT_DATATYPE_IS_MULTILAYERS(_dt) \ + ELEM(_dt, DT_TYPE_MDEFORMVERT, DT_TYPE_SHAPEKEY, DT_TYPE_VCOL, DT_TYPE_UV) + + +enum { + DT_MULTILAYER_INDEX_INVALID = -1, + DT_MULTILAYER_INDEX_MDEFORMVERT = 0, + DT_MULTILAYER_INDEX_SHAPEKEY = 1, + DT_MULTILAYER_INDEX_VCOL = 2, + DT_MULTILAYER_INDEX_UV = 3, + DT_MULTILAYER_INDEX_MAX = 4, +}; + +/* Below we keep positive values for real layers idx (generated dynamically). */ + +/* How to select data layers, for types supporting multi-layers. + * Here too, some options are highly dependent on type of transferred data! */ +enum { + DT_LAYERS_ACTIVE_SRC = -1, + DT_LAYERS_ALL_SRC = -2, + /* Datatype-specific. */ + DT_LAYERS_VGROUP_SRC = 1 << 8, + DT_LAYERS_VGROUP_SRC_BONE_SELECT = -(DT_LAYERS_VGROUP_SRC | 1), + DT_LAYERS_VGROUP_SRC_BONE_DEFORM = -(DT_LAYERS_VGROUP_SRC | 2), + /* Other types-related modes... */ +}; + +/* How to map a source layer to a destination layer, for types supporting multi-layers. + * Note: if no matching layer can be found, it will be created. */ +enum { + DT_LAYERS_ACTIVE_DST = -1, /* Only for DT_LAYERS_FROMSEL_ACTIVE. */ + DT_LAYERS_NAME_DST = -2, + DT_LAYERS_INDEX_DST = -3, +#if 0 /* TODO */ + DT_LAYERS_CREATE_DST = -4, /* Never replace existing data in dst, always create new layers. */ +#endif +}; + +void BKE_object_data_transfer_layout( + struct Scene *scene, struct Object *ob_src, struct 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]); + +bool BKE_object_data_transfer_mesh( + struct Scene *scene, + struct Object *ob_src, struct Object *ob_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_poly_mode, + struct SpaceTransform *space_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, + struct ReportList *reports); +bool BKE_object_data_transfer_dm( + struct Scene *scene, + struct Object *ob_src, struct Object *ob_dst, struct DerivedMesh *dm_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_poly_mode, + struct SpaceTransform *space_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, + struct ReportList *reports); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_OBJECT_DATA_TRANSFER_H__ */ diff --git a/source/blender/blenkernel/BKE_object_deform.h b/source/blender/blenkernel/BKE_object_deform.h index 0e5ad2d51c8..e956815d6f7 100644 --- a/source/blender/blenkernel/BKE_object_deform.h +++ b/source/blender/blenkernel/BKE_object_deform.h @@ -72,4 +72,11 @@ bool *BKE_object_defgroup_selected_get(struct Object *ob, int defbase_tot, int * } #endif +/* Select helpers */ +bool *BKE_objdef_vgroup_subset_from_select_type( + struct Object *ob, enum eVGroupSelect subset_type, int *r_vgroup_tot, int *r_subset_count); +void BKE_objdef_vgroup_subset_to_index_array( + const bool *vgroup_validmap, const int vgroup_tot, int *r_vgroup_subset_map); + + #endif /* __BKE_OBJECT_DEFORM_H__ */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 5976e041703..31e28f9e51b 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -130,6 +130,7 @@ set(SRC intern/nla.c intern/node.c intern/object.c + intern/object_data_transfer.c intern/object_deform.c intern/object_dupli.c intern/ocean.c @@ -238,6 +239,7 @@ set(SRC BKE_nla.h BKE_node.h BKE_object.h + BKE_object_data_transfer.h BKE_object_deform.h BKE_ocean.h BKE_packedFile.h @@ -277,6 +279,7 @@ set(SRC tracking_private.h intern/CCGSubSurf.h intern/pbvh_intern.h + intern/data_transfer_intern.h ) if(WITH_BINRELOC) diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 5e86ca596cf..856f893e876 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -48,6 +48,7 @@ #include "BLI_string.h" #include "BLI_path_util.h" #include "BLI_math.h" +#include "BLI_math_color_blend.h" #include "BLI_mempool.h" #include "BLF_translation.h" @@ -56,6 +57,8 @@ #include "BKE_customdata_file.h" #include "BKE_global.h" #include "BKE_main.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_remap.h" #include "BKE_multires.h" #include "bmesh.h" @@ -92,7 +95,7 @@ typedef struct LayerTypeInfo { * (deep copy if appropriate) * if NULL, memcpy is used */ - void (*copy)(const void *source, void *dest, int count); + cd_copy copy; /** * a function to free any dynamically allocated components of this @@ -117,8 +120,7 @@ typedef struct LayerTypeInfo { * applying changes while reading from sources. * See bug [#32395] - Campbell. */ - void (*interp)(void **sources, const float *weights, const float *sub_weights, - int count, void *dest); + cd_interp interp; /** a function to swap the data in corners of the element */ void (*swap)(void *data, const int *corner_indices); @@ -134,7 +136,7 @@ typedef struct LayerTypeInfo { void (*initminmax)(void *min, void *max); void (*add)(void *data1, const void *data2); void (*dominmax)(const void *data1, void *min, void *max); - void (*copyvalue)(const void *source, void *dest); + void (*copyvalue)(const void *source, void *dest, const int mixmode, const float mixfactor); /** a function to read data from a cdf file */ int (*read)(CDataFile *cdf, void *data, int count); @@ -620,14 +622,53 @@ static void layerFree_grid_paint_mask(void *data, int count, int UNUSED(size)) } /* --------- */ -static void layerCopyValue_mloopcol(const void *source, void *dest) +static void layerCopyValue_mloopcol(const void *source, void *dest, const int mixmode, const float mixfactor) { const MLoopCol *m1 = source; MLoopCol *m2 = dest; - - m2->r = m1->r; - m2->g = m1->g; - m2->b = m1->b; + unsigned char tmp_col[4]; + + if (ELEM(mixmode, CDT_MIX_NOMIX, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) { + /* Modes that do a full copy or nothing. */ + if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) { + /* TODO: Check for a real valid way to get 'factor' value of our dest color? */ + const float f = ((float)m2->r + (float)m2->g + (float)m2->b) / 3.0f; + if (mixmode == CDT_MIX_REPLACE_ABOVE_THRESHOLD && f < mixfactor) { + return; /* Do Nothing! */ + } + else if (mixmode == CDT_MIX_REPLACE_BELOW_THRESHOLD && f > mixfactor) { + return; /* Do Nothing! */ + } + } + m2->r = m1->r; + m2->g = m1->g; + m2->b = m1->b; + } + else { /* Modes that support 'real' mix factor. */ + unsigned char src[4] = {m1->r, m1->g, m1->b, m1->a}; + unsigned char dst[4] = {m2->r, m2->g, m2->b, m2->a}; + + if (mixmode == CDT_MIX_MIX) { + blend_color_mix_byte(tmp_col, dst, src); + } + else if (mixmode == CDT_MIX_ADD) { + blend_color_add_byte(tmp_col, dst, src); + } + else if (mixmode == CDT_MIX_SUB) { + blend_color_sub_byte(tmp_col, dst, src); + } + else if (mixmode == CDT_MIX_MUL) { + blend_color_mul_byte(tmp_col, dst, src); + } + else { + memcpy(tmp_col, src, sizeof(tmp_col)); + } + blend_color_interpolate_byte(dst, dst, tmp_col, mixfactor); + + m2->r = (char)dst[0]; + m2->g = (char)dst[1]; + m2->b = (char)dst[2]; + } m2->a = m1->a; } @@ -758,12 +799,19 @@ static int layerMaxNum_mloopcol(void) return MAX_MCOL; } -static void layerCopyValue_mloopuv(const void *source, void *dest) +static void layerCopyValue_mloopuv(const void *source, void *dest, const int mixmode, const float mixfactor) { const MLoopUV *luv1 = source; MLoopUV *luv2 = dest; - copy_v2_v2(luv2->uv, luv1->uv); + /* We only support a limited subset of advanced mixing here - namely the mixfactor interpolation. */ + + if (mixmode == CDT_MIX_NOMIX) { + copy_v2_v2(luv2->uv, luv1->uv); + } + else { + interp_v2_v2v2(luv2->uv, luv2->uv, luv1->uv, mixfactor); + } } static bool layerEqual_mloopuv(const void *data1, const void *data2) @@ -833,7 +881,8 @@ static void layerInterp_mloopuv(void **sources, const float *weights, } /* origspace is almost exact copy of mloopuv's, keep in sync */ -static void layerCopyValue_mloop_origspace(const void *source, void *dest) +static void layerCopyValue_mloop_origspace(const void *source, void *dest, + const int UNUSED(mixmode), const float UNUSED(mixfactor)) { const OrigSpaceLoop *luv1 = source; OrigSpaceLoop *luv2 = dest; @@ -2261,7 +2310,7 @@ int CustomData_get_n_offset(const CustomData *data, int type, int n) bool CustomData_set_layer_name(const CustomData *data, int type, int n, const char *name) { /* get the layer index of the first layer of type */ - int layer_index = CustomData_get_layer_index_n(data, type, n); + const int layer_index = CustomData_get_layer_index_n(data, type, n); if ((layer_index == -1) || !name) return false; @@ -2271,6 +2320,13 @@ bool CustomData_set_layer_name(const CustomData *data, int type, int n, const ch return true; } +const char *CustomData_get_layer_name(const CustomData *data, int type, int n) +{ + const int layer_index = CustomData_get_layer_index_n(data, type, n); + + return (layer_index == -1) ? NULL : data->layers[layer_index].name; +} + void *CustomData_set_layer(const CustomData *data, int type, void *ptr) { /* get the layer index of the first layer of type */ @@ -2764,11 +2820,28 @@ void CustomData_data_copy_value(int type, const void *source, void *dest) if (!dest) return; if (typeInfo->copyvalue) - typeInfo->copyvalue(source, dest); + typeInfo->copyvalue(source, dest, CDT_MIX_NOMIX, 0.0f); else memcpy(dest, source, typeInfo->size); } +/* Mixes the "value" (e.g. mloopuv uv or mloopcol colors) from one block into + * another, while not overwriting anything else (e.g. flags)*/ +void CustomData_data_mix_value(int type, const void *source, void *dest, const int mixmode, const float mixfactor) +{ + const LayerTypeInfo *typeInfo = layerType_getInfo(type); + + if (!dest) return; + + if (typeInfo->copyvalue) { + typeInfo->copyvalue(source, dest, mixmode, mixfactor); + } + else { + /* Mere copy if no advanced interpolation is supported. */ + memcpy(dest, source, typeInfo->size); + } +} + bool CustomData_data_equals(int type, const void *data1, const void *data2) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -3464,3 +3537,233 @@ void CustomData_external_remove_object(CustomData *data, ID *id) } #endif +/* ********** Mesh-to-mesh data transfer ********** */ +static void copy_bit_flag(void *dst, void *src, const size_t data_size, const uint64_t flag) +{ +#define COPY_BIT_FLAG(_type, _dst, _src, _f) \ +{ \ + const _type _val = *((_type *)(_src)) & ((_type)(_f)); \ + *((_type *)(_dst)) &= ~((_type)(_f)); \ + *((_type *)(_dst)) |= _val; \ +} (void) 0 + + switch (data_size) { + case 1: + COPY_BIT_FLAG(uint8_t, dst, src, flag); + break; + case 2: + COPY_BIT_FLAG(uint16_t, dst, src, flag); + break; + case 4: + COPY_BIT_FLAG(uint32_t, dst, src, flag); + break; + case 8: + COPY_BIT_FLAG(uint64_t, dst, src, flag); + break; + default: + //printf("ERROR %s: Unknown flags-container size (%zu)\n", __func__, datasize); + break; + } + +#undef COPY_BIT_FLAG +} + +static bool check_bit_flag(void *data, const size_t data_size, const uint64_t flag) +{ + switch (data_size) { + case 1: + return ((*((uint8_t *)data) & ((uint8_t)flag)) != 0); + case 2: + return ((*((uint16_t *)data) & ((uint16_t)flag)) != 0); + case 4: + return ((*((uint32_t *)data) & ((uint32_t)flag)) != 0); + case 8: + return ((*((uint64_t *)data) & ((uint64_t)flag)) != 0); + default: + //printf("ERROR %s: Unknown flags-container size (%zu)\n", __func__, datasize); + return false; + } +} + +static void customdata_data_transfer_interp_generic( + const CustomDataTransferLayerMap *laymap, void *data_dst, void **sources, const float *weights, const int count, + const float mix_factor) +{ + /* Fake interpolation, we actually copy highest weighted source to dest. + * Note we also handle bitflags here, in which case we rather choose to transfer value of elements totalizing + * more than 0.5 of weight. */ + + int best_src_idx = 0; + + const int data_type = laymap->data_type; + const int mix_mode = laymap->mix_mode; + + size_t data_size; + const uint64_t data_flag = laymap->data_flag; + + cd_interp interp_cd = NULL; + cd_copy copy_cd = NULL; + + void *tmp_dst; + + if (!sources) { + /* Not supported here, abort. */ + return; + } + + if (data_type & CD_FAKE) { + data_size = laymap->data_size; + } + else { + const LayerTypeInfo *type_info = layerType_getInfo(data_type); + + data_size = (size_t)type_info->size; + interp_cd = type_info->interp; + copy_cd = type_info->copy; + } + + tmp_dst = MEM_mallocN(data_size, __func__); + + if (count > 1 && !interp_cd) { + int i; + + if (data_flag) { + /* Boolean case, we can 'interpolate' in two groups, and choose value from highest weighted group. */ + float tot_weight_true = 0.0f; + int item_true_idx = -1, item_false_idx = -1; + + for (i = 0; i < count; i++) { + if (check_bit_flag(sources[i], data_size, data_flag)) { + tot_weight_true += weights[i]; + item_true_idx = i; + } + else { + item_false_idx = i; + } + } + best_src_idx = (tot_weight_true >= 0.5f) ? item_true_idx : item_false_idx; + } + else { + /* We just choose highest weighted source. */ + float max_weight = 0.0f; + + for (i = 0; i < count; i++) { + if (weights[i] > max_weight) { + max_weight = weights[i]; + best_src_idx = i; + } + } + } + } + + BLI_assert(best_src_idx >= 0); + + if (interp_cd) { + interp_cd(sources, weights, NULL, count, (char *)tmp_dst); + } + else if (data_flag) { + copy_bit_flag(tmp_dst, sources[best_src_idx], data_size, data_flag); + } + /* No interpolation, just copy highest weight source element's data. */ + else if (copy_cd) { + copy_cd((char *)sources[best_src_idx], (char *)tmp_dst, 1); + } + else { + memcpy(tmp_dst, sources[best_src_idx], data_size); + } + + if (data_flag) { + /* Bool flags, only copy if dest data is set (resp. unset) - only 'advanced' modes we can support here! */ + if (mix_factor >= 0.5f && + ((mix_mode == CDT_MIX_TRANSFER) || + (mix_mode == CDT_MIX_REPLACE_ABOVE_THRESHOLD && check_bit_flag(data_dst, data_size, data_flag)) || + (mix_mode == CDT_MIX_REPLACE_BELOW_THRESHOLD && !check_bit_flag(data_dst, data_size, data_flag)))) + { + copy_bit_flag(data_dst, tmp_dst, data_size, data_flag); + } + } + else if (!(data_type & CD_FAKE)) { + CustomData_data_mix_value(data_type, tmp_dst, data_dst, mix_mode, mix_factor); + } + /* Else we can do nothing by default, needs custom interp func! + * Note this is here only for sake of consistency, not expected to be used much actually? */ + else { + if (mix_factor >= 0.5f) { + memcpy(data_dst, tmp_dst, data_size); + } + } + + MEM_freeN(tmp_dst); +} + +void CustomData_data_transfer(const MeshPairRemap *me_remap, const CustomDataTransferLayerMap *laymap) +{ + MeshPairRemapItem *mapit = me_remap->items; + const int totelem = me_remap->items_num; + int i; + + const int data_type = laymap->data_type; + void *data_src = laymap->data_src; + void *data_dst = laymap->data_dst; + + size_t data_step; + size_t data_size; + size_t data_offset; + + cd_datatransfer_interp interp = NULL; + + size_t tmp_buff_size = 32; + void **tmp_data_src = NULL; + + /* Note: NULL data_src may happen and be valid (see vgroups...). */ + if (!data_dst) { + return; + } + + if (data_src) { + tmp_data_src = MEM_mallocN(sizeof(*tmp_data_src) * tmp_buff_size, __func__); + } + + if (data_type & CD_FAKE) { + data_step = laymap->elem_size; + data_size = laymap->data_size; + data_offset = laymap->data_offset; + } + else { + const LayerTypeInfo *type_info = layerType_getInfo(data_type); + + /* Note: we can use 'fake' CDLayers, like e.g. for crease, bweight, etc. :/ */ + data_size = (size_t)type_info->size; + data_step = laymap->elem_size ? laymap->elem_size : data_size; + data_offset = laymap->data_offset; + } + + interp = laymap->interp ? laymap->interp : customdata_data_transfer_interp_generic; + + for (i = 0; i < totelem; i++, data_dst = (char *)data_dst + data_step, mapit++) { + const int sources_num = mapit->sources_num; + const float mix_factor = laymap->mix_weights ? laymap->mix_weights[i] : laymap->mix_factor; + int j; + + if (!sources_num) { + /* No sources for this element, skip it. */ + continue; + } + + if (tmp_data_src) { + if (UNLIKELY(sources_num > tmp_buff_size)) { + tmp_buff_size = (size_t)sources_num; + tmp_data_src = MEM_reallocN(tmp_data_src, sizeof(*tmp_data_src) * tmp_buff_size); + } + + for (j = 0; j < sources_num; j++) { + const size_t src_idx = (size_t)mapit->indices_src[j]; + tmp_data_src[j] = (char *)data_src + data_step * src_idx + data_offset; + } + } + + interp(laymap, (char *)data_dst + data_offset, tmp_data_src, mapit->weights_src, sources_num, mix_factor); + } + + MEM_SAFE_FREE(tmp_data_src); +} diff --git a/source/blender/blenkernel/intern/data_transfer_intern.h b/source/blender/blenkernel/intern/data_transfer_intern.h new file mode 100644 index 00000000000..913325afce0 --- /dev/null +++ b/source/blender/blenkernel/intern/data_transfer_intern.h @@ -0,0 +1,60 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2014 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Bastien Montagne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/data_transfer_intern.h + * \ingroup bke + */ + +#ifndef __DATA_TRANSFER_INTERN_H__ +#define __DATA_TRANSFER_INTERN_H__ + +struct CustomDataTransferLayerMap; +struct CustomData; +struct ListBase; + +float data_transfer_interp_float_do( + const int mix_mode, const float val_dst, const float val_src, const float mix_factor); + +/* Copied from BKE_customdata.h :( */ +typedef void (*cd_datatransfer_interp)(const struct CustomDataTransferLayerMap *laymap, void *dest, + void **sources, const float *weights, const int count, const float mix_factor); + +void data_transfer_layersmapping_add_item( + struct ListBase *r_map, const int data_type, const int mix_mode, + const float mix_factor, const float *mix_weights, + 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); + +/* Type-specific. */ + +bool data_transfer_layersmapping_vgroups( + struct ListBase *r_map, 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, + struct Object *ob_src, struct Object *ob_dst, struct CustomData *cd_src, struct CustomData *cd_dst, + const bool use_dupref_dst, const int fromlayers, const int tolayers); + +#endif /* __DATA_TRANSFER_INTERN_H__ */ diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index d3148dbc8e9..4ed9278ab26 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -39,7 +39,9 @@ #include "MEM_guardedalloc.h" #include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -49,7 +51,14 @@ #include "BLF_translation.h" +#include "BKE_customdata.h" +#include "BKE_object_data_transfer.h" #include "BKE_deform.h" /* own include */ +#include "BKE_mesh_mapping.h" +#include "BKE_object_deform.h" + +#include "data_transfer_intern.h" + bDeformGroup *BKE_defgroup_new(Object *ob, const char *name) { @@ -960,3 +969,385 @@ void BKE_defvert_array_free(MDeformVert *dvert, int totvert) MEM_freeN(dvert); } + +void BKE_defvert_extract_vgroup_to_vertweights( + MDeformVert *dvert, const int defgroup, const int num_verts, float *r_weights, const bool invert_vgroup) +{ + if (dvert && defgroup != -1) { + int i = num_verts; + + while (i--) { + const float w = defvert_find_weight(&dvert[i], defgroup); + r_weights[i] = invert_vgroup ? (1.0f - w) : w; + } + } + else { + fill_vn_fl(r_weights, invert_vgroup ? 1.0f : 0.0f, num_verts); + } +} + +/* The following three make basic interpolation, using temp vert_weights array to avoid looking up same weight + * several times. */ + +void BKE_defvert_extract_vgroup_to_edgeweights( + MDeformVert *dvert, const int defgroup, const int num_verts, MEdge *edges, const int num_edges, + float *r_weights, const bool invert_vgroup) +{ + if (dvert && defgroup != -1) { + int i = num_edges; + float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); + + BKE_defvert_extract_vgroup_to_vertweights(dvert, defgroup, num_verts, tmp_weights, invert_vgroup); + + while (i--) { + MEdge *me = &edges[i]; + + r_weights[i] = (tmp_weights[me->v1] + tmp_weights[me->v2]) * 0.5f; + } + + MEM_freeN(tmp_weights); + } + else { + fill_vn_fl(r_weights, 0.0f, num_edges); + } +} + +void BKE_defvert_extract_vgroup_to_loopweights( + MDeformVert *dvert, const int defgroup, const int num_verts, MLoop *loops, const int num_loops, + float *r_weights, const bool invert_vgroup) +{ + if (dvert && defgroup != -1) { + int i = num_loops; + float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); + + BKE_defvert_extract_vgroup_to_vertweights(dvert, defgroup, num_verts, tmp_weights, invert_vgroup); + + while (i--) { + MLoop *ml = &loops[i]; + + r_weights[i] = tmp_weights[ml->v]; + } + + MEM_freeN(tmp_weights); + } + else { + fill_vn_fl(r_weights, 0.0f, num_loops); + } +} + +void BKE_defvert_extract_vgroup_to_polyweights( + MDeformVert *dvert, const int defgroup, const int num_verts, MLoop *loops, const int UNUSED(num_loops), + MPoly *polys, const int num_polys, float *r_weights, const bool invert_vgroup) +{ + if (dvert && defgroup != -1) { + int i = num_polys; + float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); + + BKE_defvert_extract_vgroup_to_vertweights(dvert, defgroup, num_verts, tmp_weights, invert_vgroup); + + while (i--) { + MPoly *mp = &polys[i]; + MLoop *ml = &loops[mp->loopstart]; + int j = mp->totloop; + float w = 0.0f; + + for (; j--; ml++) { + w += tmp_weights[ml->v]; + } + r_weights[i] = w / (float)mp->totloop; + } + + MEM_freeN(tmp_weights); + } + else { + fill_vn_fl(r_weights, 0.0f, num_polys); + } +} + +/*********** Data Transfer **********/ + +static void vgroups_datatransfer_interp(const CustomDataTransferLayerMap *laymap, void *dest, + void **sources, const float *weights, const int count, const float mix_factor) +{ + MDeformVert **data_src = (MDeformVert **)sources; + MDeformVert *data_dst = (MDeformVert *)dest; + const int idx_src = laymap->data_src_n; + const int idx_dst = laymap->data_dst_n; + + const int mix_mode = laymap->mix_mode; + + int i, j; + + MDeformWeight *dw_src; + MDeformWeight *dw_dst = defvert_find_index(data_dst, idx_dst); + float weight_src = 0.0f, weight_dst = 0.0f; + + if (sources) { + for (i = count; i--;) { + for (j = data_src[i]->totweight; j--;) { + if ((dw_src = &data_src[i]->dw[j])->def_nr == idx_src) { + weight_src += dw_src->weight * weights[i]; + break; + } + } + } + } + + if (dw_dst) { + weight_dst = dw_dst->weight; + } + else if (mix_mode == CDT_MIX_REPLACE_ABOVE_THRESHOLD) { + return; /* Do not affect destination. */ + } + + weight_src = data_transfer_interp_float_do(mix_mode, weight_dst, weight_src, mix_factor); + + CLAMP(weight_src, 0.0f, 1.0f); + + if (!dw_dst) { + defvert_add_index_notest(data_dst, idx_dst, weight_src); + } + else { + dw_dst->weight = weight_src; + } +} + +static bool data_transfer_layersmapping_vgroups_multisrc_to_dst( + ListBase *r_map, 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, + Object *ob_src, Object *ob_dst, MDeformVert *data_src, MDeformVert *data_dst, + CustomData *UNUSED(cd_src), CustomData *cd_dst, const bool UNUSED(use_dupref_dst), + const int tolayers, bool *use_layers_src, const int num_layers_src) +{ + int idx_src; + int idx_dst; + int tot_dst = BLI_listbase_count(&ob_dst->defbase); + + const size_t elem_size = sizeof(*((MDeformVert *)NULL)); + + switch (tolayers) { + case DT_LAYERS_INDEX_DST: + idx_dst = tot_dst; + + /* Find last source actually used! */ + idx_src = num_layers_src; + while (idx_src-- && !use_layers_src[idx_src]); + idx_src++; + + if (idx_dst < idx_src) { + if (!use_create) { + return false; + } + /* Create as much vgroups as necessary! */ + for (; idx_dst < idx_src; idx_dst++) { + BKE_object_defgroup_add(ob_dst); + } + } + else if (use_delete && idx_dst > idx_src) { + while (idx_dst-- > idx_src) { + BKE_object_defgroup_remove(ob_dst, ob_dst->defbase.last); + } + } + if (r_map) { + /* At this stage, we **need** a valid CD_MDEFORMVERT layer on dest! + * Again, use_create is not relevant in this case */ + if (!data_dst) { + data_dst = CustomData_add_layer(cd_dst, CD_MDEFORMVERT, CD_CALLOC, NULL, num_elem_dst); + } + + while (idx_src--) { + if (!use_layers_src[idx_src]) { + continue; + } + data_transfer_layersmapping_add_item(r_map, CD_FAKE_MDEFORMVERT, mix_mode, mix_factor, mix_weights, + data_src, data_dst, idx_src, idx_src, + elem_size, 0, 0, 0, vgroups_datatransfer_interp); + } + } + break; + case DT_LAYERS_NAME_DST: + { + bDeformGroup *dg_src, *dg_dst; + + if (use_delete) { + /* Remove all unused dst vgroups first, simpler in this case. */ + for (dg_dst = ob_dst->defbase.first; dg_dst;) { + bDeformGroup *dg_dst_next = dg_dst->next; + + if (defgroup_name_index(ob_src, dg_dst->name) == -1) { + BKE_object_defgroup_remove(ob_dst, dg_dst); + } + dg_dst = dg_dst_next; + } + } + + for (idx_src = 0, dg_src = ob_src->defbase.first; + idx_src < num_layers_src; + idx_src++, dg_src = dg_src->next) + { + if (!use_layers_src[idx_src]) { + continue; + } + + if ((idx_dst = defgroup_name_index(ob_dst, dg_src->name)) == -1) { + if (!use_create) { + if (r_map) { + BLI_freelistN(r_map); + } + return false; + } + BKE_object_defgroup_add_name(ob_dst, dg_src->name); + idx_dst = ob_dst->actdef - 1; + } + if (r_map) { + /* At this stage, we **need** a valid CD_MDEFORMVERT layer on dest! + * use_create is not relevant in this case */ + if (!data_dst) { + data_dst = CustomData_add_layer(cd_dst, CD_MDEFORMVERT, CD_CALLOC, NULL, num_elem_dst); + } + + data_transfer_layersmapping_add_item( + r_map, CD_FAKE_MDEFORMVERT, mix_mode, mix_factor, mix_weights, + data_src, data_dst, idx_src, idx_dst, + elem_size, 0, 0, 0, vgroups_datatransfer_interp); + } + } + break; + } + default: + return false; + } + + return true; +} + +bool data_transfer_layersmapping_vgroups( + ListBase *r_map, 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, Object *ob_src, Object *ob_dst, + CustomData *cd_src, CustomData *cd_dst, const bool use_dupref_dst, const int fromlayers, const int tolayers) +{ + int idx_src, idx_dst; + MDeformVert *data_src, *data_dst = NULL; + + const size_t elem_size = sizeof(*((MDeformVert *)NULL)); + + /* Note: VGroups are a bit hairy, since their layout is defined on object level (ob->defbase), while their actual + * data is a (mesh) CD layer. + * This implies we may have to handle data layout itself while having NULL data itself, + * and even have to support NULL data_src in transfer data code (we always create a data_dst, though). + */ + + if (BLI_listbase_is_empty(&ob_src->defbase)) { + if (use_delete) { + BKE_object_defgroup_remove_all(ob_dst); + } + return true; + } + + data_src = CustomData_get_layer(cd_src, CD_MDEFORMVERT); + + data_dst = CustomData_get_layer(cd_dst, CD_MDEFORMVERT); + if (data_dst && use_dupref_dst && r_map) { + /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ + data_dst = CustomData_duplicate_referenced_layer(cd_dst, CD_MDEFORMVERT, num_elem_dst); + } + + if (fromlayers == DT_LAYERS_ACTIVE_SRC || fromlayers >= 0) { + /* Note: use_delete has not much meaning in this case, ignored. */ + + if (fromlayers >= 0) { + idx_src = fromlayers; + BLI_assert(idx_src < BLI_listbase_count(&ob_src->defbase)); + } + else if ((idx_src = ob_src->actdef - 1) == -1) { + return false; + } + + if (tolayers >= 0) { + /* Note: in this case we assume layer exists! */ + idx_dst = tolayers; + BLI_assert(idx_dst < BLI_listbase_count(&ob_dst->defbase)); + } + else if (tolayers == DT_LAYERS_ACTIVE_DST) { + if ((idx_dst = ob_dst->actdef - 1) == -1) { + bDeformGroup *dg_src; + if (!use_create) { + return true; + } + dg_src = BLI_findlink(&ob_src->defbase, idx_src); + BKE_object_defgroup_add_name(ob_dst, dg_src->name); + idx_dst = ob_dst->actdef - 1; + } + } + else if (tolayers == DT_LAYERS_INDEX_DST) { + int num = BLI_listbase_count(&ob_src->defbase); + idx_dst = idx_src; + if (num <= idx_dst) { + if (!use_create) { + return true; + } + /* Create as much vgroups as necessary! */ + for (; num <= idx_dst; num++) { + BKE_object_defgroup_add(ob_dst); + } + } + } + else if (tolayers == DT_LAYERS_NAME_DST) { + bDeformGroup *dg_src = BLI_findlink(&ob_src->defbase, idx_src); + if ((idx_dst = defgroup_name_index(ob_dst, dg_src->name)) == -1) { + if (!use_create) { + return true; + } + BKE_object_defgroup_add_name(ob_dst, dg_src->name); + idx_dst = ob_dst->actdef - 1; + } + } + else { + return false; + } + + if (r_map) { + /* At this stage, we **need** a valid CD_MDEFORMVERT layer on dest! + * use_create is not relevant in this case */ + if (!data_dst) { + data_dst = CustomData_add_layer(cd_dst, CD_MDEFORMVERT, CD_CALLOC, NULL, num_elem_dst); + } + + data_transfer_layersmapping_add_item(r_map, CD_FAKE_MDEFORMVERT, mix_mode, mix_factor, mix_weights, + data_src, data_dst, idx_src, idx_dst, + elem_size, 0, 0, 0, vgroups_datatransfer_interp); + } + } + else { + int num_src, num_sel_unused; + bool *use_layers_src = NULL; + bool ret = false; + + switch (fromlayers) { + case DT_LAYERS_ALL_SRC: + use_layers_src = BKE_object_defgroup_subset_from_select_type(ob_src, WT_VGROUP_ALL, + &num_src, &num_sel_unused); + break; + case DT_LAYERS_VGROUP_SRC_BONE_SELECT: + use_layers_src = BKE_object_defgroup_subset_from_select_type(ob_src, WT_VGROUP_BONE_SELECT, + &num_src, &num_sel_unused); + break; + case DT_LAYERS_VGROUP_SRC_BONE_DEFORM: + use_layers_src = BKE_object_defgroup_subset_from_select_type(ob_src, WT_VGROUP_BONE_DEFORM, + &num_src, &num_sel_unused); + break; + } + + if (use_layers_src) { + ret = data_transfer_layersmapping_vgroups_multisrc_to_dst( + r_map, mix_mode, mix_factor, mix_weights, num_elem_dst, use_create, use_delete, + ob_src, ob_dst, data_src, data_dst, cd_src, cd_dst, use_dupref_dst, + tolayers, use_layers_src, num_src); + } + + MEM_SAFE_FREE(use_layers_src); + return ret; + } + + return true; +} diff --git a/source/blender/blenkernel/intern/object_data_transfer.c b/source/blender/blenkernel/intern/object_data_transfer.c new file mode 100644 index 00000000000..71320fe9946 --- /dev/null +++ b/source/blender/blenkernel/intern/object_data_transfer.c @@ -0,0 +1,1227 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2014 by Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Bastien Montagne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/object_data_transfer.c + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_array.h" +#include "BLI_math.h" +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_object_data_transfer.h" +#include "BKE_deform.h" +#include "BKE_DerivedMesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_remap.h" +#include "BKE_object.h" +#include "BKE_object_deform.h" +#include "BKE_report.h" + +#include "data_transfer_intern.h" + + +CustomDataMask BKE_object_data_transfer_dttypes_to_cdmask(const int dtdata_types) +{ + CustomDataMask cddata_mask = 0; + int i; + + for (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)) { + cddata_mask |= 1LL << cddata_type; + } + else if (cddata_type == CD_FAKE_MDEFORMVERT) { + cddata_mask |= CD_MASK_MDEFORMVERT; /* Exception for vgroups :/ */ + } + else if (cddata_type == CD_FAKE_UV) { + cddata_mask |= CD_MASK_MTEXPOLY | CD_MASK_MLOOPUV; + } + } + + return cddata_mask; +} + +/* Check what can do each layer type (if it is actually handled by transferdata, if it supports advanced mixing... */ +bool BKE_object_data_transfer_get_dttypes_capacity( + const int dtdata_types, bool *r_advanced_mixing, bool *r_threshold) +{ + int i; + bool ret = false; + + *r_advanced_mixing = false; + *r_threshold = false; + + for (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: + ret = true; + break; + /* Loop/Poly data */ + case DT_TYPE_UV: + ret = true; + break; + case DT_TYPE_VCOL: + *r_advanced_mixing = true; + *r_threshold = true; + ret = true; + break; + case DT_TYPE_SHARP_FACE: + ret = true; + break; + case DT_TYPE_FREESTYLE_FACE: + 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_POLY(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_VCOL: + return CD_MLOOPCOL; + + default: + BLI_assert(0); + } + 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_VCOL: + return DT_MULTILAYER_INDEX_VCOL; + default: + return DT_MULTILAYER_INDEX_INVALID; + } +} + +/* ********** */ + +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_poly_uv; + break; + default: + break; + } + return NULL; +} + +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); +} + +static void data_transfer_interp_char(const CustomDataTransferLayerMap *laymap, void *dest, + void **sources, const float *weights, const int count, const float mix_factor) +{ + char **data_src = (char **)sources; + char *data_dst = (char *)dest; + + const int mix_mode = laymap->mix_mode; + float val_src = 0.0f; + const float val_dst = (float)(*data_dst) / 255.0f; + + int i; + + for (i = count; i--;) { + val_src += ((float)(*data_src[i]) / 255.0f) * weights[i]; + } + + val_src = data_transfer_interp_float_do(mix_mode, val_dst, val_src, mix_factor); + + CLAMP(val_src, 0.0f, 1.0f); + + *data_dst = (char)(val_src * 255.0f); +} + +/* 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, + 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) +{ + CustomDataTransferLayerMap *item = MEM_mallocN(sizeof(*item), __func__); + + BLI_assert(data_dst != NULL); + + item->data_type = 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; + + 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, + void *data_src, void *data_dst) +{ + data_transfer_layersmapping_add_item( + r_map, cddata_type, mix_mode, mix_factor, mix_weights, data_src, data_dst, + 0, 0, 0, 0, 0, 0, NULL); +} + +/* 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 NULL, 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 int 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, + CustomData *cd_src, CustomData *cd_dst, const bool use_dupref_dst, + const int tolayers, bool *use_layers_src, const int num_layers_src) +{ + void *data_src, *data_dst = NULL; + int idx_src = num_layers_src; + int idx_dst, tot_dst = CustomData_number_of_layers(cd_dst, cddata_type); + bool *data_dst_to_delete = NULL; + + switch (tolayers) { + case DT_LAYERS_INDEX_DST: + idx_dst = tot_dst; + + /* Find last source actually used! */ + while (idx_src-- && !use_layers_src[idx_src]); + idx_src++; + + if (idx_dst < idx_src) { + if (!use_create) { + return true; + } + /* Create as much data layers as necessary! */ + for (; idx_dst < idx_src; idx_dst++) { + CustomData_add_layer(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst); + } + } + else if (use_delete && idx_dst > idx_src) { + while (idx_dst-- > idx_src) { + CustomData_free_layer(cd_dst, cddata_type, num_elem_dst, 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); + /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ + if (use_dupref_dst) { + data_dst = CustomData_duplicate_referenced_layer_n(cd_dst, cddata_type, idx_src, num_elem_dst); + } + else { + data_dst = CustomData_get_layer_n(cd_dst, cddata_type, idx_src); + } + data_transfer_layersmapping_add_item_cd(r_map, cddata_type, mix_mode, mix_factor, mix_weights, + data_src, data_dst); + } + } + break; + case DT_LAYERS_NAME_DST: + if (use_delete) { + if (tot_dst) { + data_dst_to_delete = MEM_mallocN(sizeof(*data_dst_to_delete) * (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); + + if ((idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name)) == -1) { + if (!use_create) { + if (r_map) { + BLI_freelistN(r_map); + } + return true; + } + CustomData_add_layer_named(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst, name); + idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name); + } + else if (data_dst_to_delete) { + data_dst_to_delete[idx_dst] = false; + } + if (r_map) { + /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ + if (use_dupref_dst) { + data_dst = CustomData_duplicate_referenced_layer_n(cd_dst, cddata_type, idx_dst, num_elem_dst); + } + else { + data_dst = CustomData_get_layer_n(cd_dst, cddata_type, idx_dst); + } + data_transfer_layersmapping_add_item_cd(r_map, cddata_type, mix_mode, mix_factor, mix_weights, + data_src, data_dst); + } + } + + 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, num_elem_dst, idx_dst); + } + } + + MEM_freeN(data_dst_to_delete); + } + break; + default: + return false; + } + + return true; +} + +static bool data_transfer_layersmapping_cdlayers( + ListBase *r_map, const int 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, + CustomData *cd_src, CustomData *cd_dst, const bool use_dupref_dst, + const int fromlayers, const int tolayers) +{ + int idx_src, idx_dst; + void *data_src, *data_dst = NULL; + + if (CustomData_layertype_is_singleton(cddata_type)) { + if (!(data_src = CustomData_get_layer(cd_src, cddata_type))) { + if (use_delete) { + CustomData_free_layer(cd_dst, cddata_type, num_elem_dst, 0); + } + return true; + } + + data_dst = CustomData_get_layer(cd_dst, cddata_type); + if (!data_dst) { + if (!use_create) { + return true; + } + data_dst = CustomData_add_layer(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst); + } + else if (use_dupref_dst && r_map) { + /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ + data_dst = CustomData_duplicate_referenced_layer(cd_dst, cddata_type, 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); + } + } + else if (fromlayers == DT_LAYERS_ACTIVE_SRC || fromlayers >= 0) { + /* Note: use_delete has not much meaning in this case, ignored. */ + + if (fromlayers >= 0) { /* Real-layer index */ + idx_src = fromlayers; + } + else { + if ((idx_src = CustomData_get_active_layer(cd_src, cddata_type)) == -1) { + return true; + } + } + data_src = CustomData_get_layer_n(cd_src, cddata_type, idx_src); + if (!data_src) { + return true; + } + + if (tolayers >= 0) { /* Real-layer index */ + idx_dst = tolayers; + /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ + if (use_dupref_dst && r_map) { + data_dst = CustomData_duplicate_referenced_layer_n(cd_dst, cddata_type, idx_dst, num_elem_dst); + } + else { + data_dst = CustomData_get_layer_n(cd_dst, cddata_type, idx_dst); + } + } + else if (tolayers == DT_LAYERS_ACTIVE_DST) { + if ((idx_dst = CustomData_get_active_layer(cd_dst, cddata_type)) == -1) { + if (!use_create) { + return true; + } + data_dst = CustomData_add_layer(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst); + } + else { + /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ + if (use_dupref_dst && r_map) { + data_dst = CustomData_duplicate_referenced_layer_n(cd_dst, cddata_type, idx_dst, num_elem_dst); + } + else { + data_dst = CustomData_get_layer_n(cd_dst, cddata_type, idx_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, cddata_type, CD_CALLOC, NULL, num_elem_dst); + } + } + /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ + if (use_dupref_dst && r_map) { + data_dst = CustomData_duplicate_referenced_layer_n(cd_dst, cddata_type, idx_dst, num_elem_dst); + } + else { + data_dst = CustomData_get_layer_n(cd_dst, cddata_type, idx_dst); + } + } + else if (tolayers == DT_LAYERS_NAME_DST) { + const char *name = CustomData_get_layer_name(cd_src, cddata_type, idx_src); + if ((idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name)) == -1) { + if (!use_create) { + return true; + } + CustomData_add_layer_named(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst, name); + idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name); + } + /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ + if (use_dupref_dst && r_map) { + data_dst = CustomData_duplicate_referenced_layer_n(cd_dst, cddata_type, idx_dst, num_elem_dst); + } + else { + data_dst = CustomData_get_layer_n(cd_dst, cddata_type, idx_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); + } + } + else if (fromlayers == DT_LAYERS_ALL_SRC) { + int num_src = CustomData_number_of_layers(cd_src, cddata_type); + bool *use_layers_src = num_src ? MEM_mallocN(sizeof(*use_layers_src) * (size_t)num_src, __func__) : NULL; + 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, use_dupref_dst, + tolayers, use_layers_src, num_src); + + 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, DerivedMesh *dm_src, DerivedMesh *dm_dst, 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) +{ + CustomData *cd_src, *cd_dst; + + if (elem_type == ME_VERT) { + if (!(cddata_type & CD_FAKE)) { + cd_src = dm_src->getVertDataLayout(dm_src); + cd_dst = dm_dst ? dm_dst->getVertDataLayout(dm_dst) : &me_dst->vdata; + + if (!data_transfer_layersmapping_cdlayers(r_map, cddata_type, mix_mode, mix_factor, mix_weights, + num_elem_dst, use_create, use_delete, + cd_src, cd_dst, dm_dst != NULL, + fromlayers, tolayers)) + { + /* We handle specific source selection cases here. */ + return false; + } + return true; + } + else if (cddata_type == CD_FAKE_BWEIGHT) { + const size_t elem_size = sizeof(*((MVert *)NULL)); + const size_t data_size = sizeof(((MVert *)NULL)->bweight); + const size_t data_offset = offsetof(MVert, bweight); + const uint64_t data_flag = 0; + + if (!(dm_src->cd_flag & ME_CDFLAG_VERT_BWEIGHT)) { + if (use_delete && !dm_dst) { + me_dst->cd_flag &= ~ME_CDFLAG_VERT_BWEIGHT; + } + return true; + } + if (dm_dst) { + dm_dst->cd_flag |= ME_CDFLAG_VERT_BWEIGHT; + } + else { + me_dst->cd_flag |= ME_CDFLAG_VERT_BWEIGHT; + } + if (r_map) { + data_transfer_layersmapping_add_item(r_map, cddata_type, mix_mode, mix_factor, mix_weights, + dm_src->getVertArray(dm_src), + dm_dst ? dm_dst->getVertArray(dm_dst) : me_dst->mvert, + dm_src->getNumVerts(dm_src), + dm_dst ? dm_dst->getNumVerts(dm_dst) : me_dst->totvert, + elem_size, data_size, data_offset, data_flag, + data_transfer_interp_char); + } + return true; + } + else if (cddata_type == CD_FAKE_MDEFORMVERT) { + cd_src = dm_src->getVertDataLayout(dm_src); + cd_dst = dm_dst ? dm_dst->getVertDataLayout(dm_dst) : &me_dst->vdata; + + return 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, dm_dst != NULL, + fromlayers, tolayers); + } + else if (cddata_type == CD_FAKE_SHAPEKEY) { + /* TODO: leaving shapekeys asside for now, quite specific case, since we can't access them from MVert :/ */ + return false; + } + } + else if (elem_type == ME_EDGE) { + if (!(cddata_type & CD_FAKE)) { /* Unused for edges, currently... */ + cd_src = dm_src->getEdgeDataLayout(dm_src); + cd_dst = dm_dst ? dm_dst->getEdgeDataLayout(dm_dst) : &me_dst->edata; + + if (!data_transfer_layersmapping_cdlayers(r_map, cddata_type, mix_mode, mix_factor, mix_weights, + num_elem_dst, use_create, use_delete, + cd_src, cd_dst, dm_dst != NULL, + fromlayers, tolayers)) + { + /* We handle specific source selection cases here. */ + return false; + } + return true; + } + else if (cddata_type == CD_FAKE_CREASE) { + const size_t elem_size = sizeof(*((MEdge *)NULL)); + const size_t data_size = sizeof(((MEdge *)NULL)->crease); + const size_t data_offset = offsetof(MEdge, crease); + const uint64_t data_flag = 0; + + if (!(dm_src->cd_flag & ME_CDFLAG_EDGE_CREASE)) { + if (use_delete && !dm_dst) { + me_dst->cd_flag &= ~ME_CDFLAG_EDGE_CREASE; + } + return true; + } + if (dm_dst) { + dm_dst->cd_flag |= ME_CDFLAG_EDGE_CREASE; + } + else { + me_dst->cd_flag |= ME_CDFLAG_EDGE_CREASE; + } + if (r_map) { + data_transfer_layersmapping_add_item(r_map, cddata_type, mix_mode, mix_factor, mix_weights, + dm_src->getEdgeArray(dm_src), + dm_dst ? dm_dst->getEdgeArray(dm_dst) : me_dst->medge, + dm_src->getNumEdges(dm_src), + dm_dst ? dm_dst->getNumEdges(dm_dst) : me_dst->totedge, + elem_size, data_size, data_offset, data_flag, + data_transfer_interp_char); + } + return true; + } + else if (cddata_type == CD_FAKE_BWEIGHT) { + const size_t elem_size = sizeof(*((MEdge *)NULL)); + const size_t data_size = sizeof(((MEdge *)NULL)->bweight); + const size_t data_offset = offsetof(MEdge, bweight); + const uint64_t data_flag = 0; + + if (!(dm_src->cd_flag & ME_CDFLAG_EDGE_BWEIGHT)) { + if (use_delete && !dm_dst) { + me_dst->cd_flag &= ~ME_CDFLAG_EDGE_BWEIGHT; + } + return true; + } + if (dm_dst) { + dm_dst->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT; + } + else { + me_dst->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT; + } + if (r_map) { + data_transfer_layersmapping_add_item(r_map, cddata_type, mix_mode, mix_factor, mix_weights, + dm_src->getEdgeArray(dm_src), + dm_dst ? dm_dst->getEdgeArray(dm_dst) : me_dst->medge, + dm_src->getNumEdges(dm_src), + dm_dst ? dm_dst->getNumEdges(dm_dst) : me_dst->totedge, + elem_size, data_size, data_offset, data_flag, + data_transfer_interp_char); + } + return true; + } + else if (r_map && ELEM(cddata_type, CD_FAKE_SHARP, CD_FAKE_SEAM)) { + const size_t elem_size = sizeof(*((MEdge *)NULL)); + const size_t data_size = sizeof(((MEdge *)NULL)->flag); + const size_t data_offset = offsetof(MEdge, flag); + const uint64_t data_flag = (cddata_type == CD_FAKE_SHARP) ? ME_SHARP : ME_SEAM; + + data_transfer_layersmapping_add_item( + r_map, cddata_type, mix_mode, mix_factor, mix_weights, + dm_src->getEdgeArray(dm_src), + dm_dst ? dm_dst->getEdgeArray(dm_dst) : me_dst->medge, + dm_src->getNumEdges(dm_src), + dm_dst ? dm_dst->getNumEdges(dm_dst) : me_dst->totedge, + elem_size, data_size, data_offset, data_flag, NULL); + return true; + } + else { + return false; + } + } + else if (elem_type == ME_LOOP) { + if (cddata_type == CD_FAKE_UV) { + cddata_type = CD_MLOOPUV; + } + + if (!(cddata_type & CD_FAKE)) { + cd_src = dm_src->getLoopDataLayout(dm_src); + cd_dst = dm_dst ? dm_dst->getLoopDataLayout(dm_dst) : &me_dst->ldata; + + if (!data_transfer_layersmapping_cdlayers( + r_map, cddata_type, mix_mode, mix_factor, mix_weights, + num_elem_dst, use_create, use_delete, cd_src, cd_dst, dm_dst != NULL, + fromlayers, tolayers)) + { + /* We handle specific source selection cases here. */ + return false; + } + return true; + } + else { + return false; + } + } + else if (elem_type == ME_POLY) { + if (cddata_type == CD_FAKE_UV) { + cddata_type = CD_MTEXPOLY; + } + + if (!(cddata_type & CD_FAKE)) { + cd_src = dm_src->getPolyDataLayout(dm_src); + cd_dst = dm_dst ? dm_dst->getPolyDataLayout(dm_dst) : &me_dst->pdata; + + if (!data_transfer_layersmapping_cdlayers( + r_map, cddata_type, mix_mode, mix_factor, mix_weights, + num_elem_dst, use_create, use_delete, cd_src, cd_dst, dm_dst != NULL, + fromlayers, tolayers)) + { + /* We handle specific source selection cases here. */ + return false; + } + return true; + } + else if (r_map && cddata_type == CD_FAKE_SHARP) { + const size_t elem_size = sizeof(*((MPoly *)NULL)); + const size_t data_size = sizeof(((MPoly *)NULL)->flag); + const size_t data_offset = offsetof(MPoly, flag); + const uint64_t data_flag = ME_SMOOTH; + + data_transfer_layersmapping_add_item( + r_map, cddata_type, mix_mode, mix_factor, mix_weights, + dm_src->getPolyArray(dm_src), + dm_dst ? dm_dst->getPolyArray(dm_dst) : me_dst->mpoly, + dm_src->getNumPolys(dm_src), + dm_dst ? dm_dst->getNumPolys(dm_dst) : me_dst->totpoly, + elem_size, data_size, data_offset, data_flag, NULL); + return true; + } + else { + return false; + } + } + + return false; +} + +/** + * Transfer data *layout* of selected types from source to destination object. + * By default, it only creates new data layers if needed on \a ob_dst. + * If \a use_delete is true, it will also delete data layers on \a ob_dst that do not match those from \a ob_src, + * to get (as much as possible) exact copy of source data layout. + */ +void BKE_object_data_transfer_layout( + Scene *scene, 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]) +{ + DerivedMesh *dm_src; + Mesh *me_dst; + int i; + + const bool use_create = true; /* We always create needed layers here. */ + + CustomDataMask dm_src_mask = CD_MASK_BAREMESH; + + BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH)); + + me_dst = ob_dst->data; + + /* Get source DM.*/ + dm_src_mask |= BKE_object_data_transfer_dttypes_to_cdmask(data_types); + dm_src = mesh_get_derived_final(scene, ob_src, dm_src_mask); + if (!dm_src) { + return; + } + + /* Check all possible data types. */ + for (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->totvert; + + data_transfer_layersmapping_generate( + NULL, ob_src, ob_dst, dm_src, NULL, me_dst, ME_VERT, cddata_type, 0, 0.0f, NULL, + num_elem_dst, use_create, use_delete, fromlayers, tolayers); + } + if (DT_DATATYPE_IS_EDGE(dtdata_type)) { + const int num_elem_dst = me_dst->totedge; + + data_transfer_layersmapping_generate( + NULL, ob_src, ob_dst, dm_src, NULL, me_dst, ME_EDGE, cddata_type, 0, 0.0f, NULL, + num_elem_dst, use_create, use_delete, fromlayers, tolayers); + } + if (DT_DATATYPE_IS_LOOP(dtdata_type)) { + const int num_elem_dst = me_dst->totloop; + + data_transfer_layersmapping_generate( + NULL, ob_src, ob_dst, dm_src, NULL, me_dst, ME_LOOP, cddata_type, 0, 0.0f, NULL, + num_elem_dst, use_create, use_delete, fromlayers, tolayers); + } + if (DT_DATATYPE_IS_POLY(dtdata_type)) { + const int num_elem_dst = me_dst->totpoly; + + data_transfer_layersmapping_generate( + NULL, ob_src, ob_dst, dm_src, NULL, me_dst, ME_POLY, cddata_type, 0, 0.0f, NULL, + num_elem_dst, use_create, use_delete, fromlayers, tolayers); + } + } +} + +bool BKE_object_data_transfer_dm( + Scene *scene, Object *ob_src, Object *ob_dst, DerivedMesh *dm_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_poly_mode, + SpaceTransform *space_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 + + DerivedMesh *dm_src; + Mesh *me_dst; + bool dirty_nors_dst = true; /* Assumed always true if not using a dm as destination. */ + int i; + + MDeformVert *mdef = NULL; + int vg_idx = -1; + float *weights[DATAMAX] = {NULL}; + + MeshPairRemap geom_map[DATAMAX] = {{0}}; + bool geom_map_init[DATAMAX] = {0}; + ListBase lay_map = {0}; + bool changed = false; + + const bool use_delete = false; /* We never delete data layers from destination here. */ + + CustomDataMask dm_src_mask = CD_MASK_BAREMESH; + + BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH)); + + me_dst = ob_dst->data; + if (dm_dst) { + dirty_nors_dst = (dm_dst->dirty & DM_DIRTY_NORMALS) != 0; + use_create = false; /* Never create needed custom layers on DM (modifier case). */ + } + + if (vgroup_name) { + if (dm_dst) { + mdef = dm_dst->getVertDataArray(dm_dst, CD_MDEFORMVERT); + } + else { + mdef = CustomData_get_layer(&me_dst->vdata, CD_MDEFORMVERT); + } + if (mdef) { + vg_idx = defgroup_name_index(ob_dst, vgroup_name); + } + } + + /* Get source DM.*/ + dm_src_mask |= BKE_object_data_transfer_dttypes_to_cdmask(data_types); + /* XXX Hack! In case this is being evaluated from dm stack, we cannot compute final dm, + * can lead to infinite recursion in case of dependency cycles of DataTransfer modifiers... + * Issue is, this means we cannot be sure to have requested cd layers in source. + */ + dm_src = dm_dst ? ob_src->derivedFinal : mesh_get_derived_final(scene, ob_src, dm_src_mask); + if (!dm_src) { + return changed; + } + + /* Check all possible data types. + * Note item mappings and dest mix weights are cached. */ + for (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)) { + MVert *verts_dst = dm_dst ? dm_dst->getVertArray(dm_dst) : me_dst->mvert; + const int num_verts_dst = dm_dst ? dm_dst->getNumVerts(dm_dst) : me_dst->totvert; + + if (!geom_map_init[VDATA]) { + if ((map_vert_mode == MREMAP_MODE_TOPOLOGY) && (num_verts_dst != dm_src->getNumVerts(dm_src))) { + BKE_report(reports, RPT_ERROR, + "Source and destination meshes do not have the same amount of vertices, " + "'Topology' mapping cannot be used in this case"); + return changed; + } + BKE_mesh_remap_calc_verts_from_dm( + map_vert_mode, space_transform, max_distance, ray_radius, + verts_dst, num_verts_dst, dirty_nors_dst, dm_src, &geom_map[VDATA]); + geom_map_init[VDATA] = true; + } + + if (mdef && vg_idx != -1 && !weights[VDATA]) { + weights[VDATA] = MEM_mallocN(sizeof(*(weights[VDATA])) * (size_t)num_verts_dst, __func__); + BKE_defvert_extract_vgroup_to_vertweights(mdef, vg_idx, num_verts_dst, weights[VDATA], invert_vgroup); + } + + if (data_transfer_layersmapping_generate( + &lay_map, ob_src, ob_dst, dm_src, dm_dst, me_dst, ME_VERT, + cddata_type, mix_mode, mix_factor, weights[VDATA], + num_verts_dst, use_create, use_delete, fromlayers, tolayers)) + { + CustomDataTransferLayerMap *lay_mapit; + + changed = (lay_map.first != NULL); + + for (lay_mapit = lay_map.first; lay_mapit; lay_mapit = lay_mapit->next) { + CustomData_data_transfer(&geom_map[VDATA], lay_mapit); + } + + BLI_freelistN(&lay_map); + } + } + if (DT_DATATYPE_IS_EDGE(dtdata_type)) { + MVert *verts_dst = dm_dst ? dm_dst->getVertArray(dm_dst) : me_dst->mvert; + const int num_verts_dst = dm_dst ? dm_dst->getNumVerts(dm_dst) : me_dst->totvert; + MEdge *edges_dst = dm_dst ? dm_dst->getEdgeArray(dm_dst) : me_dst->medge; + const int num_edges_dst = dm_dst ? dm_dst->getNumEdges(dm_dst) : me_dst->totedge; + + if (!geom_map_init[EDATA]) { + if ((map_edge_mode == MREMAP_MODE_TOPOLOGY) && (num_edges_dst != dm_src->getNumEdges(dm_src))) { + BKE_report(reports, RPT_ERROR, + "Source and destination meshes do not have the same amount of edges, " + "'Topology' mapping cannot be used in this case"); + return changed; + } + BKE_mesh_remap_calc_edges_from_dm( + map_edge_mode, space_transform, max_distance, ray_radius, + verts_dst, num_verts_dst, edges_dst, num_edges_dst, dirty_nors_dst, + dm_src, &geom_map[EDATA]); + geom_map_init[EDATA] = true; + } + + if (mdef && vg_idx != -1 && !weights[EDATA]) { + weights[EDATA] = MEM_mallocN(sizeof(*weights[EDATA]) * (size_t)num_edges_dst, __func__); + BKE_defvert_extract_vgroup_to_edgeweights( + mdef, vg_idx, num_verts_dst, edges_dst, num_edges_dst, + weights[EDATA], invert_vgroup); + } + + if (data_transfer_layersmapping_generate( + &lay_map, ob_src, ob_dst, dm_src, dm_dst, me_dst, ME_EDGE, + cddata_type, mix_mode, mix_factor, weights[EDATA], + num_edges_dst, use_create, use_delete, fromlayers, tolayers)) + { + CustomDataTransferLayerMap *lay_mapit; + + changed = (lay_map.first != NULL); + + for (lay_mapit = lay_map.first; lay_mapit; lay_mapit = lay_mapit->next) { + CustomData_data_transfer(&geom_map[EDATA], lay_mapit); + } + + BLI_freelistN(&lay_map); + } + } + if (DT_DATATYPE_IS_LOOP(dtdata_type)) { + MVert *verts_dst = dm_dst ? dm_dst->getVertArray(dm_dst) : me_dst->mvert; + const int num_verts_dst = dm_dst ? dm_dst->getNumVerts(dm_dst) : me_dst->totvert; + MEdge *edges_dst = dm_dst ? dm_dst->getEdgeArray(dm_dst) : me_dst->medge; + const int num_edges_dst = dm_dst ? dm_dst->getNumEdges(dm_dst) : me_dst->totedge; + MPoly *polys_dst = dm_dst ? dm_dst->getPolyArray(dm_dst) : me_dst->mpoly; + const int num_polys_dst = dm_dst ? dm_dst->getNumPolys(dm_dst) : me_dst->totpoly; + MLoop *loops_dst = dm_dst ? dm_dst->getLoopArray(dm_dst) : me_dst->mloop; + const int num_loops_dst = dm_dst ? dm_dst->getNumLoops(dm_dst) : me_dst->totloop; + CustomData *pdata_dst = dm_dst ? dm_dst->getPolyDataLayout(dm_dst) : &me_dst->pdata; + CustomData *ldata_dst = dm_dst ? dm_dst->getLoopDataLayout(dm_dst) : &me_dst->ldata; + + MeshRemapIslandsCalc island_callback = data_transfer_get_loop_islands_generator(cddata_type); + + if (!geom_map_init[LDATA]) { + if ((map_loop_mode == MREMAP_MODE_TOPOLOGY) && (num_loops_dst != dm_src->getNumLoops(dm_src))) { + BKE_report(reports, RPT_ERROR, + "Source and destination meshes do not have the same amount of face corners, " + "'Topology' mapping cannot be used in this case"); + return changed; + } + BKE_mesh_remap_calc_loops_from_dm( + map_loop_mode, space_transform, max_distance, ray_radius, + verts_dst, num_verts_dst, edges_dst, num_edges_dst, + loops_dst, num_loops_dst, polys_dst, num_polys_dst, + ldata_dst, pdata_dst, me_dst->smoothresh, dirty_nors_dst, + dm_src, island_callback, islands_handling_precision, &geom_map[LDATA]); + geom_map_init[LDATA] = true; + } + + if (mdef && vg_idx != -1 && !weights[LDATA]) { + weights[LDATA] = MEM_mallocN(sizeof(*weights[LDATA]) * (size_t)num_loops_dst, __func__); + BKE_defvert_extract_vgroup_to_loopweights( + mdef, vg_idx, num_verts_dst, loops_dst, num_loops_dst, + weights[LDATA], invert_vgroup); + } + + if (data_transfer_layersmapping_generate( + &lay_map, ob_src, ob_dst, dm_src, dm_dst, me_dst, ME_LOOP, + cddata_type, mix_mode, mix_factor, weights[LDATA], + num_loops_dst, use_create, use_delete, fromlayers, tolayers)) + { + CustomDataTransferLayerMap *lay_mapit; + + changed = (lay_map.first != NULL); + + for (lay_mapit = lay_map.first; lay_mapit; lay_mapit = lay_mapit->next) { + CustomData_data_transfer(&geom_map[LDATA], lay_mapit); + } + + BLI_freelistN(&lay_map); + } + } + if (DT_DATATYPE_IS_POLY(dtdata_type)) { + MVert *verts_dst = dm_dst ? dm_dst->getVertArray(dm_dst) : me_dst->mvert; + const int num_verts_dst = dm_dst ? dm_dst->getNumVerts(dm_dst) : me_dst->totvert; + MPoly *polys_dst = dm_dst ? dm_dst->getPolyArray(dm_dst) : me_dst->mpoly; + const int num_polys_dst = dm_dst ? dm_dst->getNumPolys(dm_dst) : me_dst->totpoly; + MLoop *loops_dst = dm_dst ? dm_dst->getLoopArray(dm_dst) : me_dst->mloop; + const int num_loops_dst = dm_dst ? dm_dst->getNumLoops(dm_dst) : me_dst->totloop; + CustomData *pdata_dst = dm_dst ? dm_dst->getPolyDataLayout(dm_dst) : &me_dst->pdata; + + if (!geom_map_init[PDATA]) { + if ((map_poly_mode == MREMAP_MODE_TOPOLOGY) && (num_polys_dst != dm_src->getNumPolys(dm_src))) { + BKE_report(reports, RPT_ERROR, + "Source and destination meshes do not have the same amount of faces, " + "'Topology' mapping cannot be used in this case"); + return changed; + } + BKE_mesh_remap_calc_polys_from_dm( + map_poly_mode, space_transform, max_distance, ray_radius, + verts_dst, num_verts_dst, loops_dst, num_loops_dst, + polys_dst, num_polys_dst, pdata_dst, dirty_nors_dst, + dm_src, &geom_map[PDATA]); + geom_map_init[PDATA] = true; + } + + if (mdef && vg_idx != -1 && !weights[PDATA]) { + weights[PDATA] = MEM_mallocN(sizeof(*weights[PDATA]) * (size_t)num_polys_dst, __func__); + BKE_defvert_extract_vgroup_to_polyweights( + mdef, vg_idx, num_verts_dst, loops_dst, num_loops_dst, + polys_dst, num_polys_dst, weights[PDATA], invert_vgroup); + } + + if (data_transfer_layersmapping_generate( + &lay_map, ob_src, ob_dst, dm_src, dm_dst, me_dst, ME_POLY, + cddata_type, mix_mode, mix_factor, weights[PDATA], + num_polys_dst, use_create, use_delete, fromlayers, tolayers)) + { + CustomDataTransferLayerMap *lay_mapit; + + changed = (lay_map.first != NULL); + + for (lay_mapit = lay_map.first; lay_mapit; lay_mapit = lay_mapit->next) { + CustomData_data_transfer(&geom_map[PDATA], lay_mapit); + } + + BLI_freelistN(&lay_map); + } + } + } + + for (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( + Scene *scene, 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_poly_mode, + SpaceTransform *space_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_dm( + scene, ob_src, ob_dst, NULL, data_types, use_create, + map_vert_mode, map_edge_mode, map_loop_mode, map_poly_mode, space_transform, + max_distance, ray_radius, islands_handling_precision, fromlayers_select, tolayers_select, + mix_mode, mix_factor, vgroup_name, invert_vgroup, reports); +} diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 9b380ff8d48..79437bb05b9 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -56,6 +56,7 @@ set(SRC object_relations.c object_select.c object_shapekey.c + object_data_transfer.c object_transform.c object_warp.c object_vgroup.c diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c new file mode 100644 index 00000000000..1d994746ba6 --- /dev/null +++ b/source/blender/editors/object/object_data_transfer.c @@ -0,0 +1,620 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2014 by Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Bastien Montagne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/object/object_transfer_data.c + * \ingroup edobj + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_math.h" +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_object_data_transfer.h" +#include "BKE_DerivedMesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_remap.h" +#include "BKE_object.h" +#include "BKE_report.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_object.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "object_intern.h" + +/* All possible data to transfer. + * Note some are 'fake' ones, i.e. they are not hold by real CDLayers. */ +/* Not shared with modifier, since we use a usual enum here, not a multi-choice one. */ +static EnumPropertyItem DT_layer_items[] = { + {0, "", 0, "Vertex Data", ""}, + {DT_TYPE_MDEFORMVERT, "VGROUP_WEIGHTS", 0, "Vertex Group(s)", "Transfer active or all vertex groups"}, +#if 0 /* XXX For now, would like to finish/merge work from 2014 gsoc first. */ + {DT_TYPE_SHAPEKEY, "SHAPEKEYS", 0, "Shapekey(s)", "Transfer active or all shape keys"}, +#endif +#if 0 /* XXX When SkinModifier is enabled, it seems to erase its own CD_MVERT_SKIN layer from final DM :( */ + {DT_TYPE_SKIN, "SKIN", 0, "Skin Weight", "Transfer skin weights"}, +#endif + {DT_TYPE_BWEIGHT_VERT, "BEVEL_WEIGHT_VERT", 0, "Bevel Weight", "Transfer bevel weights"}, + {0, "", 0, "Edge Data", ""}, + {DT_TYPE_SHARP_EDGE, "SHARP_EDGE", 0, "Sharp", "Transfer sharp mark"}, + {DT_TYPE_SEAM, "SEAM", 0, "UV Seam", "Transfer UV seam mark"}, + {DT_TYPE_CREASE, "CREASE", 0, "Subsurf Crease", "Transfer crease values"}, + {DT_TYPE_BWEIGHT_EDGE, "BEVEL_WEIGHT_EDGE", 0, "Bevel Weight", "Transfer bevel weights"}, + {DT_TYPE_FREESTYLE_EDGE, "FREESTYLE_EDGE", 0, "Freestyle Mark", "Transfer Freestyle edge mark"}, + {0, "", 0, "Face Corner Data", ""}, + {DT_TYPE_VCOL, "VCOL", 0, "VCol", "Vertex (face corners) colors"}, + {DT_TYPE_UV, "UV", 0, "UVs", "Transfer UV layers"}, + {0, "", 0, "Face Data", ""}, + {DT_TYPE_SHARP_FACE, "SMOOTH", 0, "Smooth", "Transfer flat/smooth mark"}, + {DT_TYPE_FREESTYLE_FACE, "FREESTYLE_FACE", 0, "Freestyle Mark", "Transfer Freestyle face mark"}, + {0, NULL, 0, NULL, NULL} +}; + +/* Note: DT_layers_select_src_items enum is from rna_modifier.c */ +static EnumPropertyItem *dt_layers_select_src_itemf( + bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + EnumPropertyItem *item = NULL, tmp_item = {0}; + int totitem = 0; + + const int data_type = RNA_enum_get(ptr, "data_type"); + + if (!C) { /* needed for docs and i18n tools */ + return DT_layers_select_src_items; + } + + RNA_enum_items_add_value(&item, &totitem, DT_layers_select_src_items, DT_LAYERS_ACTIVE_SRC); + RNA_enum_items_add_value(&item, &totitem, DT_layers_select_src_items, DT_LAYERS_ALL_SRC); + + if (data_type == DT_TYPE_MDEFORMVERT) { + Object *ob_src = CTX_data_active_object(C); + + if (BKE_object_pose_armature_get(ob_src)) { + RNA_enum_items_add_value(&item, &totitem, DT_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_SELECT); + RNA_enum_items_add_value(&item, &totitem, DT_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_DEFORM); + } + + if (ob_src) { + bDeformGroup *dg; + int i; + + RNA_enum_item_add_separator(&item, &totitem); + + for (i = 0, dg = ob_src->defbase.first; dg; i++, dg = dg->next) { + tmp_item.value = i; + tmp_item.identifier = tmp_item.name = dg->name; + RNA_enum_item_add(&item, &totitem, &tmp_item); + } + } + } + else if (data_type == DT_TYPE_SHAPEKEY) { + /* TODO */ + } + else if (data_type == DT_TYPE_UV) { + Object *ob_src = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + + if (ob_src) { + DerivedMesh *dm_src; + CustomData *pdata; + int num_data, i; + + /* XXX Is this OK? */ + dm_src = mesh_get_derived_final(scene, ob_src, CD_MASK_BAREMESH | CD_MTEXPOLY); + pdata = dm_src->getPolyDataLayout(dm_src); + num_data = CustomData_number_of_layers(pdata, CD_MTEXPOLY); + + RNA_enum_item_add_separator(&item, &totitem); + + for (i = 0; i < num_data; i++) { + tmp_item.value = i; + tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(pdata, CD_MTEXPOLY, i); + RNA_enum_item_add(&item, &totitem, &tmp_item); + } + } + } + else if (data_type == DT_TYPE_VCOL) { + Object *ob_src = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + + if (ob_src) { + DerivedMesh *dm_src; + CustomData *ldata; + int num_data, i; + + /* XXX Is this OK? */ + dm_src = mesh_get_derived_final(scene, ob_src, CD_MASK_BAREMESH | CD_MLOOPCOL); + ldata = dm_src->getLoopDataLayout(dm_src); + num_data = CustomData_number_of_layers(ldata, CD_MLOOPCOL); + + RNA_enum_item_add_separator(&item, &totitem); + + for (i = 0; i < num_data; i++) { + tmp_item.value = i; + tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(ldata, CD_MLOOPCOL, i); + RNA_enum_item_add(&item, &totitem, &tmp_item); + } + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +/* Note: DT_layers_select_dst_items enum is from rna_modifier.c */ +static EnumPropertyItem *dt_layers_select_dst_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + EnumPropertyItem *item = NULL; + int totitem = 0; + + const int layers_select_src = RNA_enum_get(ptr, "layers_select_src"); + + if (!C) { /* needed for docs and i18n tools */ + return DT_layers_select_dst_items; + } + + if (layers_select_src == DT_LAYERS_ACTIVE_SRC || layers_select_src >= 0) { + RNA_enum_items_add_value(&item, &totitem, DT_layers_select_dst_items, DT_LAYERS_ACTIVE_DST); + } + RNA_enum_items_add_value(&item, &totitem, DT_layers_select_dst_items, DT_LAYERS_NAME_DST); + RNA_enum_items_add_value(&item, &totitem, DT_layers_select_dst_items, DT_LAYERS_INDEX_DST); + + /* No 'specific' to-layers here, since we may transfer to several objects at once! */ + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +/* Note: DT_mix_mode_items enum is from rna_modifier.c */ +static EnumPropertyItem *dt_mix_mode_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + EnumPropertyItem *item = NULL; + int totitem = 0; + + const int dtdata_type = RNA_enum_get(ptr, "data_type"); + bool support_advanced_mixing, support_threshold; + + if (!C) { /* needed for docs and i18n tools */ + return DT_mix_mode_items; + } + + RNA_enum_items_add_value(&item, &totitem, DT_mix_mode_items, CDT_MIX_TRANSFER); + + BKE_object_data_transfer_get_dttypes_capacity(dtdata_type, &support_advanced_mixing, &support_threshold); + + if (support_advanced_mixing) { + RNA_enum_items_add_value(&item, &totitem, DT_mix_mode_items, CDT_MIX_REPLACE_ABOVE_THRESHOLD); + RNA_enum_items_add_value(&item, &totitem, DT_mix_mode_items, CDT_MIX_REPLACE_BELOW_THRESHOLD); + } + + if (support_advanced_mixing) { + RNA_enum_item_add_separator(&item, &totitem); + RNA_enum_items_add_value(&item, &totitem, DT_mix_mode_items, CDT_MIX_MIX); + RNA_enum_items_add_value(&item, &totitem, DT_mix_mode_items, CDT_MIX_ADD); + RNA_enum_items_add_value(&item, &totitem, DT_mix_mode_items, CDT_MIX_SUB); + RNA_enum_items_add_value(&item, &totitem, DT_mix_mode_items, CDT_MIX_MUL); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +static bool data_transfer_check(bContext *UNUSED(C), wmOperator *op) +{ + const int layers_select_src = RNA_enum_get(op->ptr, "layers_select_src"); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "layers_select_dst"); + const int layers_select_dst = RNA_property_enum_get(op->ptr, prop); + + /* TODO: check for invalid layers_src select modes too! */ + + if ((layers_select_src != DT_LAYERS_ACTIVE_SRC) && (layers_select_dst == DT_LAYERS_ACTIVE_DST)) { + RNA_property_enum_set(op->ptr, prop, DT_LAYERS_NAME_DST); + return true; + } + + return false; +} + +/* Helper, used by both data_transfer_exec and datalayout_transfer_exec. */ +static void data_transfer_exec_preprocess_objects(bContext *C, wmOperator *op, Object *ob_src, ListBase *ctx_objects) +{ + CollectionPointerLink *ctx_ob; + CTX_data_selected_editable_objects(C, ctx_objects); + + for (ctx_ob = ctx_objects->first; ctx_ob; ctx_ob = ctx_ob->next) { + Object *ob = ctx_ob->ptr.data; + Mesh *me; + if ((ob == ob_src) || (ob->type != OB_MESH)) { + continue; + } + + me = ob->data; + if (me->id.lib) { + /* Do not transfer to linked data, not supported. */ + BKE_reportf(op->reports, RPT_WARNING, "Skipping object '%s', linked data '%s' cannot be modified", + ob->id.name + 2, me->id.name + 2); + me->id.flag &= ~LIB_DOIT; + continue; + } + + me->id.flag |= LIB_DOIT; + } +} + +/* Helper, used by both data_transfer_exec and datalayout_transfer_exec. */ +static bool data_transfer_exec_is_object_valid(wmOperator *op, Object *ob_src, Object *ob_dst) +{ + Mesh *me; + if ((ob_dst == ob_src) || (ob_dst->type != OB_MESH)) { + return false; + } + + me = ob_dst->data; + if (me->id.flag & LIB_DOIT) { + me->id.flag &= ~LIB_DOIT; + return true; + } + else if (me->id.lib == NULL) { + /* Do not transfer apply operation more than once. */ + /* XXX This is not nice regarding vgroups, which are half-Object data... :/ */ + BKE_reportf(op->reports, RPT_WARNING, + "Skipping object '%s', data '%s' has already been processed with a previous object", + ob_dst->id.name + 2, me->id.name + 2); + } + return false; +} + +static int data_transfer_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob_src = CTX_data_active_object(C); + + ListBase ctx_objects; + CollectionPointerLink *ctx_ob_dst; + + bool changed = false; + + const int data_type = RNA_enum_get(op->ptr, "data_type"); + const bool use_create = RNA_boolean_get(op->ptr, "use_create"); + + const int map_vert_mode = RNA_enum_get(op->ptr, "vert_mapping"); + const int map_edge_mode = RNA_enum_get(op->ptr, "edge_mapping"); + const int map_loop_mode = RNA_enum_get(op->ptr, "loop_mapping"); + const int map_poly_mode = RNA_enum_get(op->ptr, "poly_mapping"); + + const bool use_object_transform = RNA_boolean_get(op->ptr, "use_object_transform"); + const bool use_max_distance = RNA_boolean_get(op->ptr, "use_max_distance"); + const float max_distance = use_max_distance ? RNA_float_get(op->ptr, "max_distance") : FLT_MAX; + const float ray_radius = RNA_float_get(op->ptr, "ray_radius"); + const float islands_precision = RNA_float_get(op->ptr, "islands_precision"); + + const int layers_src = RNA_enum_get(op->ptr, "layers_select_src"); + const int layers_dst = RNA_enum_get(op->ptr, "layers_select_dst"); + int layers_select_src[DT_MULTILAYER_INDEX_MAX] = {0}; + int layers_select_dst[DT_MULTILAYER_INDEX_MAX] = {0}; + const int fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(data_type); + + const int mix_mode = RNA_enum_get(op->ptr, "mix_mode"); + const float mix_factor = RNA_float_get(op->ptr, "mix_factor"); + + SpaceTransform space_transform_data; + SpaceTransform *space_transform = use_object_transform ? &space_transform_data : NULL; + + if (fromto_idx != DT_MULTILAYER_INDEX_INVALID) { + layers_select_src[fromto_idx] = layers_src; + layers_select_dst[fromto_idx] = layers_dst; + } + + data_transfer_exec_preprocess_objects(C, op, ob_src, &ctx_objects); + + for (ctx_ob_dst = ctx_objects.first; ctx_ob_dst; ctx_ob_dst = ctx_ob_dst->next) { + Object *ob_dst = ctx_ob_dst->ptr.data; + if (data_transfer_exec_is_object_valid(op, ob_src, ob_dst)) { + if (space_transform) { + BLI_SPACE_TRANSFORM_SETUP(space_transform, ob_dst, ob_src); + } + + if (BKE_object_data_transfer_mesh( + scene, ob_src, ob_dst, data_type, use_create, + map_vert_mode, map_edge_mode, map_loop_mode, map_poly_mode, + space_transform, max_distance, ray_radius, islands_precision, + layers_select_src, layers_select_dst, + mix_mode, mix_factor, NULL, false, op->reports)) + { + changed = true; + } + } + } + + BLI_freelistN(&ctx_objects); + +#if 0 /* TODO */ + /* Note: issue with that is that if canceled, operator cannot be redone... Nasty in our case. */ + return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +#else + return OPERATOR_FINISHED; +#endif +} + +/* Used by both OBJECT_OT_data_transfer and OBJECT_OT_datalayout_transfer */ +static int data_transfer_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + ID *data = (ob) ? ob->data : NULL; + return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib); +} + +/* Used by both OBJECT_OT_data_transfer and OBJECT_OT_datalayout_transfer */ +static bool data_transfer_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +{ + PropertyRNA *prop_other; + + const char *prop_id = RNA_property_identifier(prop); + const int data_type = RNA_enum_get(ptr, "data_type"); + bool use_max_distance = false; + bool use_modifier = false; + + if ((prop_other = RNA_struct_find_property(ptr, "use_max_distance"))) { + use_max_distance = RNA_property_boolean_get(ptr, prop_other); + } + if ((prop_other = RNA_struct_find_property(ptr, "modifier"))) { + use_modifier = RNA_property_is_set(ptr, prop_other); + } + + if (STREQ(prop_id, "modifier")) { + return use_modifier; + } + + if (use_modifier) { + /* Hide everything but 'modifier' property, if set. */ + return false; + } + + if (STREQ(prop_id, "max_distance") && !use_max_distance) { + return false; + } + if (STREQ(prop_id, "islands_precision") && !DT_DATATYPE_IS_LOOP(data_type)) { + return false; + } + + if (STREQ(prop_id, "vert_mapping") && !DT_DATATYPE_IS_VERT(data_type)) { + return false; + } + if (STREQ(prop_id, "edge_mapping") && !DT_DATATYPE_IS_EDGE(data_type)) { + return false; + } + if (STREQ(prop_id, "loop_mapping") && !DT_DATATYPE_IS_LOOP(data_type)) { + return false; + } + if (STREQ(prop_id, "poly_mapping") && !DT_DATATYPE_IS_POLY(data_type)) { + return false; + } + + if ((STREQ(prop_id, "layers_select_src") || STREQ(prop_id, "layers_select_dst")) && + !DT_DATATYPE_IS_MULTILAYERS(data_type)) + { + return false; + } + + /* Else, show it! */ + return true; +} + +/* Used by both OBJECT_OT_data_transfer and OBJECT_OT_datalayout_transfer */ +static void data_transfer_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr; + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* Main auto-draw call */ + uiDefAutoButsRNA(layout, &ptr, data_transfer_draw_check_prop, '\0'); +} + +/* transfers weight from active to selected */ +void OBJECT_OT_data_transfer(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* Identifiers.*/ + ot->name = "Transfer Mesh Data"; + ot->idname = "OBJECT_OT_data_transfer"; + ot->description = "Transfer data layer(s) (weights, edge sharp, ...) from active to selected meshes"; + + /* API callbacks.*/ + ot->poll = data_transfer_poll; + ot->invoke = WM_menu_invoke; + ot->exec = data_transfer_exec; + ot->check = data_transfer_check; + ot->ui = data_transfer_ui; + + /* Flags.*/ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties.*/ + /* Data type to transfer. */ + ot->prop = RNA_def_enum(ot->srna, "data_type", DT_layer_items, 0, "Data Type", "Which data to transfer"); + RNA_def_boolean(ot->srna, "use_create", true, "Create Data", "Add data layers on destination meshes if needed"); + + /* Mapping methods. */ + RNA_def_enum(ot->srna, "vert_mapping", DT_method_vertex_items, MREMAP_MODE_VERT_NEAREST, "Vertex Mapping", + "Method used to map source vertices to destination ones"); + RNA_def_enum(ot->srna, "edge_mapping", DT_method_edge_items, MREMAP_MODE_EDGE_NEAREST, "Edge Mapping", + "Method used to map source edges to destination ones"); + RNA_def_enum(ot->srna, "loop_mapping", DT_method_loop_items, MREMAP_MODE_LOOP_NEAREST_POLYNOR, + "Face Corner Mapping", "Method used to map source faces' corners to destination ones"); + RNA_def_enum(ot->srna, "poly_mapping", DT_method_poly_items, MREMAP_MODE_POLY_NEAREST, "Face Mapping", + "Method used to map source faces to destination ones"); + + /* Mapping options and filtering. */ + RNA_def_boolean(ot->srna, "use_object_transform", true, "Object Transform", + "Evaluate source and destination meshes in their respective object spaces"); + RNA_def_boolean(ot->srna, "use_max_distance", false, "Only Neighbor Geometry", + "Source elements must be closer than given distance from destination one"); + prop = RNA_def_float(ot->srna, "max_distance", 1.0f, 0.0f, FLT_MAX, "Max Distance", + "Maximum allowed distance between source and destination element, for non-topology mappings", + 0.0f, 100.0f); + RNA_def_property_subtype(prop, PROP_DISTANCE); + prop = RNA_def_float(ot->srna, "ray_radius", 0.0f, 0.0f, FLT_MAX, "Ray Radius", + "'Width' of rays (especially useful when raycasting against vertices or edges)", + 0.0f, 10.0f); + RNA_def_property_subtype(prop, PROP_DISTANCE); + prop = RNA_def_float(ot->srna, "islands_precision", 0.1f, 0.0f, 10.0f, "Islands Precision", + "Factor controlling precision of islands handling (the higher, the better the results)", + 0.0f, 1.0f); + RNA_def_property_subtype(prop, PROP_FACTOR); + + /* How to handle multi-layers types of data. */ + prop = RNA_def_enum(ot->srna, "layers_select_src", DT_layers_select_src_items, DT_LAYERS_ACTIVE_SRC, + "Source Layers Selection", "Which layers to transfer, in case of multi-layers types"); + RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, dt_layers_select_src_itemf); + + prop = RNA_def_enum(ot->srna, "layers_select_dst", DT_layers_select_dst_items, DT_LAYERS_ACTIVE_DST, + "Destination Layers Matching", "How to match source and destination layers"); + RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, dt_layers_select_dst_itemf); + + prop = RNA_def_enum(ot->srna, "mix_mode", DT_mix_mode_items, CDT_MIX_TRANSFER, "Mix Mode", + "How to affect destination elements with source values"); + RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, dt_mix_mode_itemf); + RNA_def_float(ot->srna, "mix_factor", 1.0f, 0.0f, 1.0f, "Mix Factor", + "Factor to use when applying data to destination (exact behavior depends on mix mode)", 0.0f, 1.0f); +} + +/******************************************************************************/ +/* Note: This operator is hybrid, it can work as a usual standalone Object operator, + * or as a DataTransfer modifier tool. + */ + +static int datalayout_transfer_poll(bContext *C) +{ + return (data_transfer_poll(C)); +} + +static int datalayout_transfer_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob_act = ED_object_active_context(C); + + { + Object *ob_src = ob_act; + + ListBase ctx_objects; + CollectionPointerLink *ctx_ob_dst; + + const int data_type = RNA_enum_get(op->ptr, "data_type"); + const bool use_delete = RNA_boolean_get(op->ptr, "use_delete"); + + const int layers_src = RNA_enum_get(op->ptr, "layers_select_src"); + const int layers_dst = RNA_enum_get(op->ptr, "layers_select_dst"); + int layers_select_src[DT_MULTILAYER_INDEX_MAX] = {0}; + int layers_select_dst[DT_MULTILAYER_INDEX_MAX] = {0}; + const int fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(data_type); + + if (fromto_idx != DT_MULTILAYER_INDEX_INVALID) { + layers_select_src[fromto_idx] = layers_src; + layers_select_dst[fromto_idx] = layers_dst; + } + + data_transfer_exec_preprocess_objects(C, op, ob_src, &ctx_objects); + + for (ctx_ob_dst = ctx_objects.first; ctx_ob_dst; ctx_ob_dst = ctx_ob_dst->next) { + Object *ob_dst = ctx_ob_dst->ptr.data; + if (data_transfer_exec_is_object_valid(op, ob_src, ob_dst)) { + BKE_object_data_transfer_layout(scene, ob_src, ob_dst, data_type, use_delete, + layers_select_src, layers_select_dst); + } + } + + BLI_freelistN(&ctx_objects); + } + + return OPERATOR_FINISHED; +} + +static int datalayout_transfer_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + return WM_menu_invoke(C, op, event); +} + +void OBJECT_OT_datalayout_transfer(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Transfer Mesh Datalayout"; + ot->description = "Transfer layout of data layer(s) from active to selected meshes"; + ot->idname = "OBJECT_OT_datalayout_transfer"; + + ot->poll = datalayout_transfer_poll; + ot->invoke = datalayout_transfer_invoke; + ot->exec = datalayout_transfer_exec; + ot->check = data_transfer_check; + ot->ui = data_transfer_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties.*/ + /* edit_modifier_properties(ot); */ + + /* Data type to transfer. */ + ot->prop = RNA_def_enum(ot->srna, "data_type", DT_layer_items, 0, "Data Type", "Which data to transfer"); + RNA_def_boolean(ot->srna, "use_delete", false, "Exact Match", + "Also delete some data layers from destination if necessary, so that it matches exactly source"); + + /* How to handle multi-layers types of data. */ + prop = RNA_def_enum(ot->srna, "layers_select_src", DT_layers_select_src_items, DT_LAYERS_ACTIVE_SRC, + "Source Layers Selection", "Which layers to transfer, in case of multi-layers types"); + RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, dt_layers_select_src_itemf); + + prop = RNA_def_enum(ot->srna, "layers_select_dst", DT_layers_select_dst_items, DT_LAYERS_ACTIVE_DST, + "Destination Layers Matching", "How to match source and destination layers"); + RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, dt_layers_select_dst_itemf); +} diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 6fa3caa6172..bcf77d819fe 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -266,5 +266,9 @@ void OBJECT_OT_lod_remove(struct wmOperatorType *ot); /* object_random.c */ void OBJECT_OT_vertex_random(struct wmOperatorType *ot); +/* object_transfer_data.c */ +void OBJECT_OT_data_transfer(struct wmOperatorType *ot); +void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot); + #endif /* __OBJECT_INTERN_H__ */ diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 2062b85fe4b..5189594c919 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -249,6 +249,9 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_lod_remove); WM_operatortype_append(OBJECT_OT_vertex_random); + + WM_operatortype_append(OBJECT_OT_data_transfer); + WM_operatortype_append(OBJECT_OT_datalayout_transfer); } void ED_operatormacros_object(void) @@ -420,6 +423,10 @@ void ED_keymap_object(wmKeyConfig *keyconf) WM_keymap_add_menu(keymap, "VIEW3D_MT_object_specials", WKEY, KM_PRESS, 0, 0); + WM_keymap_verify_item(keymap, "OBJECT_OT_data_transfer", TKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + /* XXX No more available 'T' shortcuts... :/ */ + /* WM_keymap_verify_item(keymap, "OBJECT_OT_datalayout_transfer", TKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); */ + for (i = 0; i <= 5; i++) { kmi = WM_keymap_add_item(keymap, "OBJECT_OT_subdivision_set", ZEROKEY + i, KM_PRESS, KM_CTRL, 0); RNA_int_set(kmi->ptr, "level", i); diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 3d4ed0d6f51..4d836601ac4 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -184,6 +184,14 @@ extern EnumPropertyItem linestyle_geometry_modifier_type_items[]; extern EnumPropertyItem window_cursor_items[]; +extern EnumPropertyItem DT_method_vertex_items[]; +extern EnumPropertyItem DT_method_edge_items[]; +extern EnumPropertyItem DT_method_loop_items[]; +extern EnumPropertyItem DT_method_poly_items[]; +extern EnumPropertyItem DT_mix_mode_items[]; +extern EnumPropertyItem DT_layers_select_src_items[]; +extern EnumPropertyItem DT_layers_select_dst_items[]; + struct bContext; struct PointerRNA; struct PropertyRNA; diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index cc7c9782927..1cd646b1914 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -43,7 +43,11 @@ #include "BLF_translation.h" #include "BKE_animsys.h" +#include "BKE_object_data_transfer.h" +#include "BKE_DerivedMesh.h" #include "BKE_dynamicpaint.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_remap.h" #include "BKE_multires.h" #include "BKE_smoke.h" /* For smokeModifier_free & smokeModifier_createType */ @@ -127,6 +131,110 @@ EnumPropertyItem modifier_triangulate_ngon_method_items[] = { {0, NULL, 0, NULL, NULL} }; +/* ***** Data Transfer ***** */ + +EnumPropertyItem DT_method_vertex_items[] = { + {MREMAP_MODE_TOPOLOGY, "TOPOLOGY", 0, "Topology", + "Copy from identical topology meshes"}, + {MREMAP_MODE_VERT_NEAREST, "NEAREST", 0, "Nearest vertex", + "Copy from closest vertex"}, + {MREMAP_MODE_VERT_EDGE_NEAREST, "EDGE_NEAREST", 0, "Nearest Edge Vertex", + "Copy from closest vertex of closest edge"}, + {MREMAP_MODE_VERT_EDGEINTERP_NEAREST, "EDGEINTERP_NEAREST", 0, "Nearest Edge Interpolated", + "Copy from interpolated values of vertices from closest point on closest edge"}, + {MREMAP_MODE_VERT_POLY_NEAREST, "POLY_NEAREST", 0, "Nearest Face Vertex", + "Copy from closest vertex of closest face"}, + {MREMAP_MODE_VERT_POLYINTERP_NEAREST, "POLYINTERP_NEAREST", 0, "Nearest Face Interpolated", + "Copy from interpolated values of vertices from closest point on closest face"}, + {MREMAP_MODE_VERT_POLYINTERP_VNORPROJ, "POLYINTERP_VNORPROJ", 0, "Projected Face Interpolated", + "Copy from interpolated values of vertices from point on closest face hit by normal-projection"}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem DT_method_edge_items[] = { + {MREMAP_MODE_TOPOLOGY, "TOPOLOGY", 0, "Topology", + "Copy from identical topology meshes"}, + {MREMAP_MODE_EDGE_VERT_NEAREST, "VERT_NEAREST", 0, "Nearest Vertices", + "Copy from most similar edge (edge which vertices are the closest of destination edge’s ones)"}, + {MREMAP_MODE_EDGE_NEAREST, "NEAREST", 0, "Nearest Edge", + "Copy from closest edge (using midpoints)"}, + {MREMAP_MODE_EDGE_POLY_NEAREST, "POLY_NEAREST", 0, "Nearest Face Edge", + "Copy from closest edge of closest face (using midpoints)"}, + {MREMAP_MODE_EDGE_EDGEINTERP_VNORPROJ, "EDGEINTERP_VNORPROJ", 0, "Projected Edge Interpolated", + "Interpolate all source edges hit by the projection of dest one along its own normal (from vertices)"}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem DT_method_loop_items[] = { + {MREMAP_MODE_TOPOLOGY, "TOPOLOGY", 0, "Topology", + "Copy from identical topology meshes"}, + {MREMAP_MODE_LOOP_NEAREST_LOOPNOR, "NEAREST_NORMAL", 0, "Nearest Corner And Best Matching Normal", + "Copy from nearest corner which has the best matching normal"}, + {MREMAP_MODE_LOOP_NEAREST_POLYNOR, "NEAREST_POLYNOR", 0, "Nearest Corner And Best Matching Face Normal", + "Copy from nearest corner which has the face with the best matching normal to dest corner's face one"}, + {MREMAP_MODE_LOOP_POLY_NEAREST, "NEAREST_POLY", 0, "Nearest Corner Of Nearest Face", + "Copy from nearest corner of nearest polygon"}, + {MREMAP_MODE_LOOP_POLYINTERP_NEAREST, "POLYINTERP_NEAREST", 0, "Nearest Face Interpolated", + "Copy from interpolated corners of the nearest source polygon"}, + {MREMAP_MODE_LOOP_POLYINTERP_LNORPROJ, "POLYINTERP_LNORPROJ", 0, "Projected Face Interpolated", + "Copy from interpolated corners of the source polygon hit by corner normal projection"}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem DT_method_poly_items[] = { + {MREMAP_MODE_TOPOLOGY, "TOPOLOGY", 0, "Topology", + "Copy from identical topology meshes"}, + {MREMAP_MODE_POLY_NEAREST, "NEAREST", 0, "Nearest Face", + "Copy from nearest polygon (using center points)"}, + {MREMAP_MODE_POLY_NOR, "NORMAL", 0, "Best Normal-Matching", + "Copy from source polygon which normal is the closest to dest one"}, + {MREMAP_MODE_POLY_POLYINTERP_PNORPROJ, "POLYINTERP_PNORPROJ", 0, "Projected Face Interpolated", + "Interpolate all source polygons intersected by the projection of dest one along its own normal"}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem DT_mix_mode_items[] = { + {CDT_MIX_TRANSFER, "REPLACE", 0, "Replace", + "Overwrite all elements' data"}, + {CDT_MIX_REPLACE_ABOVE_THRESHOLD, "ABOVE_THRESHOLD", 0, "Above Threshold", + "Only replace dest elements where data is above given threshold (exact behavior depends on data type)"}, + {CDT_MIX_REPLACE_BELOW_THRESHOLD, "BELOW_THRESHOLD", 0, "Below Threshold", + "Only replace dest elements where data is below given threshold (exact behavior depends on data type)"}, + {CDT_MIX_MIX, "MIX", 0, "Mix", + "Mix source value into destination one, using given threshold as factor"}, + {CDT_MIX_ADD, "ADD", 0, "Add", + "Add source value to destination one, using given threshold as factor"}, + {CDT_MIX_SUB, "SUB", 0, "Subtract", + "Subtract source value to destination one, using given threshold as factor"}, + {CDT_MIX_MUL, "MUL", 0, "Multiply", + "Multiply source value to destination one, using given threshold as factor"}, + /* etc. etc. */ + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem DT_layers_select_src_items[] = { + {DT_LAYERS_ACTIVE_SRC, "ACTIVE", 0, "Active Layer", + "Only transfer active data layer"}, + {DT_LAYERS_ALL_SRC, "ALL", 0, "All Layers", + "Transfer all data layers"}, + {DT_LAYERS_VGROUP_SRC_BONE_SELECT, "BONE_SELECT", 0, "Selected Pose Bones", + "Transfer all vertex groups used by selected posebones"}, + {DT_LAYERS_VGROUP_SRC_BONE_DEFORM, "BONE_DEFORM", 0, "Deform Pose Bones", + "Transfer all vertex groups used by deform bones"}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem DT_layers_select_dst_items[] = { + {DT_LAYERS_ACTIVE_DST, "ACTIVE", 0, "Active Layer", + "Affect active data layer of all targets"}, + {DT_LAYERS_NAME_DST, "NAME", 0, "By Name", + "Match target data layers to affect by name"}, + {DT_LAYERS_INDEX_DST, "INDEX", 0, "By Order", + "Match target data layers to affect by order (indices)"}, + {0, NULL, 0, NULL, NULL} +}; + + #ifdef RNA_RUNTIME #include "DNA_particle_types.h" |