Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py2
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py7
-rw-r--r--source/blender/blenkernel/BKE_customdata.h88
-rw-r--r--source/blender/blenkernel/BKE_deform.h17
-rw-r--r--source/blender/blenkernel/BKE_object_data_transfer.h158
-rw-r--r--source/blender/blenkernel/BKE_object_deform.h7
-rw-r--r--source/blender/blenkernel/CMakeLists.txt3
-rw-r--r--source/blender/blenkernel/intern/customdata.c331
-rw-r--r--source/blender/blenkernel/intern/data_transfer_intern.h60
-rw-r--r--source/blender/blenkernel/intern/deform.c391
-rw-r--r--source/blender/blenkernel/intern/object_data_transfer.c1227
-rw-r--r--source/blender/editors/object/CMakeLists.txt1
-rw-r--r--source/blender/editors/object/object_data_transfer.c620
-rw-r--r--source/blender/editors/object/object_intern.h4
-rw-r--r--source/blender/editors/object/object_ops.c7
-rw-r--r--source/blender/makesrna/RNA_enum_types.h8
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c108
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"