Surface Deform Modifier (SDef)
authorLuca Rood <dev@lucarood.com>
Mon, 27 Feb 2017 15:39:14 +0000 (12:39 -0300)
committerLuca Rood <dev@lucarood.com>
Mon, 27 Feb 2017 16:49:14 +0000 (13:49 -0300)
Implementation of the SDef modifier, which allows meshes to be bound by
surface, thus allowing things such as cloth simulation proxies.

User documentation: https://wiki.blender.org/index.php/User:Lucarood/SurfaceDeform

Reviewers: mont29, sergey

Subscribers: Severin, dfelinto, plasmasolutions, kjym3

Differential Revision: https://developer.blender.org/D2462

14 files changed:
release/scripts/startup/bl_ui/properties_data_modifier.py
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/object/object_intern.h
source/blender/editors/object/object_modifier.c
source/blender/editors/object/object_ops.c
source/blender/editors/space_outliner/outliner_draw.c
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_modifier.c
source/blender/modifiers/CMakeLists.txt
source/blender/modifiers/MOD_modifiertypes.h
source/blender/modifiers/intern/MOD_surfacedeform.c [new file with mode: 0644]
source/blender/modifiers/intern/MOD_util.c

index b24b574f37bc7e216aac29c3893ad6863c8fb213..d62c20d458925239a66995cb635e2e75c52eb4cd 100644 (file)
@@ -951,6 +951,20 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
     def SURFACE(self, layout, ob, md):
         layout.label(text="Settings are inside the Physics tab")
 
+    def SURFACE_DEFORM(self, layout, ob, md):
+        col = layout.column()
+        col.active = not md.is_bound
+
+        col.prop(md, "target")
+        col.prop(md, "falloff")
+
+        layout.separator()
+
+        if md.is_bound:
+            layout.operator("object.surfacedeform_bind", text="Unbind")
+        else:
+            layout.operator("object.surfacedeform_bind", text="Bind")
+
     def UV_PROJECT(self, layout, ob, md):
         split = layout.split()
 
index 7ea79fc5557f8762fe7207d26cf7ca38f6cc467a..638f877c841e41439b5662f0d8c91af9455ebe68 100644 (file)
@@ -5317,6 +5317,37 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
                        MeshSeqCacheModifierData *msmcd = (MeshSeqCacheModifierData *)md;
                        msmcd->reader = NULL;
                }
+               else if (md->type == eModifierType_SurfaceDeform) {
+                       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+
+                       smd->verts = newdataadr(fd, smd->verts);
+
+                       if (smd->verts) {
+                               for (int i = 0; i < smd->numverts; i++) {
+                                       smd->verts[i].binds = newdataadr(fd, smd->verts[i].binds);
+
+                                       if (smd->verts[i].binds) {
+                                               for (int j = 0; j < smd->verts[i].numbinds; j++) {
+                                                       smd->verts[i].binds[j].vert_inds = newdataadr(fd, smd->verts[i].binds[j].vert_inds);
+                                                       smd->verts[i].binds[j].vert_weights = newdataadr(fd, smd->verts[i].binds[j].vert_weights);
+
+                                                       if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
+                                                               if (smd->verts[i].binds[j].vert_inds)
+                                                                       BLI_endian_switch_uint32_array(smd->verts[i].binds[j].vert_inds, smd->verts[i].binds[j].numverts);
+
+                                                               if (smd->verts[i].binds[j].vert_weights) {
+                                                                       if (smd->verts[i].binds[j].mode == MOD_SDEF_MODE_CENTROID ||
+                                                                           smd->verts[i].binds[j].mode == MOD_SDEF_MODE_LOOPTRI)
+                                                                               BLI_endian_switch_float_array(smd->verts[i].binds[j].vert_weights, 3);
+                                                                       else
+                                                                               BLI_endian_switch_float_array(smd->verts[i].binds[j].vert_weights, smd->verts[i].binds[j].numverts);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
        }
 }
 
index 38a032f490790f287e99902922eb98b306249fe4..01d07e0d77486a4cec2ba9984c037b257ae0180e 100644 (file)
@@ -1837,6 +1837,32 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
                                writedata(wd, DATA, sizeof(float[3]) * csmd->bind_coords_num, csmd->bind_coords);
                        }
                }
+               else if (md->type == eModifierType_SurfaceDeform) {
+                       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+
+                       writestruct(wd, DATA, SDefVert, smd->numverts, smd->verts);
+
+                       if (smd->verts) {
+                               for (int i = 0; i < smd->numverts; i++) {
+                                       writestruct(wd, DATA, SDefBind, smd->verts[i].numbinds, smd->verts[i].binds);
+
+                                       if (smd->verts[i].binds) {
+                                               for (int j = 0; j < smd->verts[i].numbinds; j++) {
+                                                       writedata(wd, DATA, sizeof(int) * smd->verts[i].binds[j].numverts, smd->verts[i].binds[j].vert_inds);
+
+                                                       if (smd->verts[i].binds[j].mode == MOD_SDEF_MODE_CENTROID ||
+                                                           smd->verts[i].binds[j].mode == MOD_SDEF_MODE_LOOPTRI)
+                                                       {
+                                                               writedata(wd, DATA, sizeof(float) * 3, smd->verts[i].binds[j].vert_weights);
+                                                       }
+                                                       else {
+                                                               writedata(wd, DATA, sizeof(float) * smd->verts[i].binds[j].numverts, smd->verts[i].binds[j].vert_weights);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
        }
 }
 
index 9710e4f843df45098625e3c481e286866e76eb4e..b8957bdedf9ee7cf83c49f19edeb538f667d8f30 100644 (file)
@@ -186,6 +186,7 @@ void OBJECT_OT_skin_loose_mark_clear(struct wmOperatorType *ot);
 void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot);
 void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot);
 void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
+void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
 
 /* object_constraint.c */
 void OBJECT_OT_constraint_add(struct wmOperatorType *ot);
index 06f495fb9f15016c1d1023045cb9e4bafb6ce7e2..38df1116cd332217e686958e6f53399ffaca8e47 100644 (file)
@@ -2294,3 +2294,63 @@ void OBJECT_OT_laplaciandeform_bind(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
        edit_modifier_properties(ot);
 }
+
+/************************ sdef bind operator *********************/
+
+static int surfacedeform_bind_poll(bContext *C)
+{
+       if (edit_modifier_poll_generic(C, &RNA_SurfaceDeformModifier, 0)) {
+               PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_SurfaceDeformModifier);
+               SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)ptr.data;
+
+               return ((smd != NULL) && (smd->target != NULL));
+       }
+
+       return 0;
+}
+
+static int surfacedeform_bind_exec(bContext *C, wmOperator *op)
+{
+       Object *ob = ED_object_active_context(C);
+       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)edit_modifier_property_get(op, ob, eModifierType_SurfaceDeform);
+
+       if (!smd)
+               return OPERATOR_CANCELLED;
+
+       if (smd->flags & MOD_SDEF_BIND) {
+               smd->flags &= ~MOD_SDEF_BIND;
+       }
+       else if (smd->target) {
+               smd->flags |= MOD_SDEF_BIND;
+       }
+
+       DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+
+       return OPERATOR_FINISHED;
+}
+
+static int surfacedeform_bind_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+       if (edit_modifier_invoke_properties(C, op))
+               return surfacedeform_bind_exec(C, op);
+       else
+               return OPERATOR_CANCELLED;
+}
+
+void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Surface Deform Bind";
+       ot->description = "Bind mesh to target in surface deform modifier";
+       ot->idname = "OBJECT_OT_surfacedeform_bind";
+
+       /* api callbacks */
+       ot->poll = surfacedeform_bind_poll;
+       ot->invoke = surfacedeform_bind_invoke;
+       ot->exec = surfacedeform_bind_exec;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+       edit_modifier_properties(ot);
+}
index 7e7e1ef182cc4e2c9c7ad57b7371ab34a95c9400..5fe5a88435483b8c433abff82826dad8a4c5db28 100644 (file)
@@ -255,6 +255,7 @@ void ED_operatortypes_object(void)
 
        WM_operatortype_append(OBJECT_OT_data_transfer);
        WM_operatortype_append(OBJECT_OT_datalayout_transfer);
+       WM_operatortype_append(OBJECT_OT_surfacedeform_bind);
 }
 
 void ED_operatormacros_object(void)
index 99242fd12f9e7c9561ddf32b11e8d9c7bdee0ccf..684a1f9fd670bbdd692deef88dd018c70f367087 100644 (file)
@@ -1126,6 +1126,7 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
                                        case eModifierType_Cast:
                                                UI_icon_draw(x, y, ICON_MOD_CAST); break;
                                        case eModifierType_MeshDeform:
+                                       case eModifierType_SurfaceDeform:
                                                UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break;
                                        case eModifierType_Bevel:
                                                UI_icon_draw(x, y, ICON_MOD_BEVEL); break;
index f95533a88f9fca8f2cc2451e44d60bddd70b816f..0c17909db2364232ef0bf6a47c2ff05afaac9113 100644 (file)
@@ -86,6 +86,7 @@ typedef enum ModifierType {
        eModifierType_NormalEdit        = 50,
        eModifierType_CorrectiveSmooth  = 51,
        eModifierType_MeshSequenceCache = 52,
+       eModifierType_SurfaceDeform     = 53,
        NUM_MODIFIER_TYPES
 } ModifierType;
 
@@ -1570,6 +1571,45 @@ enum {
        MOD_MESHSEQ_READ_COLOR = (1 << 3),
 };
 
+typedef struct SDefBind {
+       unsigned int *vert_inds;
+       unsigned int numverts;
+       int mode;
+       float *vert_weights;
+       float normal_dist;
+       float influence;
+} SDefBind;
+
+typedef struct SDefVert {
+       SDefBind *binds;
+       unsigned int numbinds;
+       char pad[4];
+} SDefVert;
+
+typedef struct SurfaceDeformModifierData {
+       ModifierData modifier;
+
+       struct Object *target;  /* bind target object */
+       SDefVert *verts;                /* vertex bind data */
+       float falloff;
+       unsigned int numverts, numpoly;
+       int flags;
+} SurfaceDeformModifierData;
+
+/* Surface Deform modifier flags */
+enum {
+       MOD_SDEF_BIND = (1 << 0),
+       MOD_SDEF_USES_LOOPTRI = (1 << 1),
+       MOD_SDEF_HAS_CONCAVE = (1 << 2),
+};
+
+/* Surface Deform vertex bind modes */
+enum {
+       MOD_SDEF_MODE_LOOPTRI = 0,
+       MOD_SDEF_MODE_NGON = 1,
+       MOD_SDEF_MODE_CENTROID = 2,
+};
+
 #define MOD_MESHSEQ_READ_ALL \
        (MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
 
index 66e6f30feeb39fe3bcfca84c4bdcfbf2bfd46570..f9aaec69ce7f165e46ccf5d5ffb58b59037f6a93 100644 (file)
@@ -598,6 +598,7 @@ extern StructRNA RNA_StucciTexture;
 extern StructRNA RNA_SubsurfModifier;
 extern StructRNA RNA_SunLamp;
 extern StructRNA RNA_SurfaceCurve;
+extern StructRNA RNA_SurfaceDeformModifier;
 extern StructRNA RNA_SurfaceModifier;
 extern StructRNA RNA_TexMapping;
 extern StructRNA RNA_Text;
index c4f0db38a1615b5465c2a0a96c393dbdfbbc2191..47c4b425155bd72428d478ab7d2d93d5f9297b15 100644 (file)
@@ -105,6 +105,7 @@ EnumPropertyItem rna_enum_object_modifier_type_items[] = {
        {eModifierType_Shrinkwrap, "SHRINKWRAP", ICON_MOD_SHRINKWRAP, "Shrinkwrap", ""},
        {eModifierType_SimpleDeform, "SIMPLE_DEFORM", ICON_MOD_SIMPLEDEFORM, "Simple Deform", ""},
        {eModifierType_Smooth, "SMOOTH", ICON_MOD_SMOOTH, "Smooth", ""},
+       {eModifierType_SurfaceDeform, "SURFACE_DEFORM", ICON_MOD_MESHDEFORM, "Surface Deform", ""},
        {eModifierType_Warp, "WARP", ICON_MOD_WARP, "Warp", ""},
        {eModifierType_Wave, "WAVE", ICON_MOD_WAVE, "Wave", ""},
        {0, "", 0, N_("Simulate"), ""},
@@ -408,6 +409,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr)
                        return &RNA_CorrectiveSmoothModifier;
                case eModifierType_MeshSequenceCache:
                        return &RNA_MeshSequenceCacheModifier;
+               case eModifierType_SurfaceDeform:
+                       return &RNA_SurfaceDeformModifier;
                /* Default */
                case eModifierType_None:
                case eModifierType_ShapeKey:
@@ -573,6 +576,7 @@ RNA_MOD_OBJECT_SET(MeshDeform, object, OB_MESH);
 RNA_MOD_OBJECT_SET(NormalEdit, target, OB_EMPTY);
 RNA_MOD_OBJECT_SET(Shrinkwrap, target, OB_MESH);
 RNA_MOD_OBJECT_SET(Shrinkwrap, auxTarget, OB_MESH);
+RNA_MOD_OBJECT_SET(SurfaceDeform, target, OB_MESH);
 
 static void rna_HookModifier_object_set(PointerRNA *ptr, PointerRNA value)
 {
@@ -1131,6 +1135,11 @@ static int rna_CorrectiveSmoothModifier_is_bind_get(PointerRNA *ptr)
        return (csmd->bind_coords != NULL);
 }
 
+static int rna_SurfaceDeformModifier_is_bound_get(PointerRNA *ptr)
+{
+       return (((SurfaceDeformModifierData *)ptr->data)->verts != NULL);
+}
+
 static void rna_MeshSequenceCache_object_path_update(Main *bmain, Scene *scene, PointerRNA *ptr)
 {
 #ifdef WITH_ALEMBIC
@@ -4702,6 +4711,33 @@ static void rna_def_modifier_normaledit(BlenderRNA *brna)
        RNA_def_property_update(prop, 0, "rna_Modifier_update");
 }
 
+static void rna_def_modifier_surfacedeform(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna = RNA_def_struct(brna, "SurfaceDeformModifier", "Modifier");
+       RNA_def_struct_ui_text(srna, "SurfaceDeform Modifier", "blablabla");
+       RNA_def_struct_sdna(srna, "SurfaceDeformModifierData");
+       RNA_def_struct_ui_icon(srna, ICON_MOD_MESHDEFORM);
+
+       prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
+       RNA_def_property_ui_text(prop, "Target", "Mesh object to deform with");
+       RNA_def_property_pointer_funcs(prop, NULL, "rna_SurfaceDeformModifier_target_set", NULL, "rna_Mesh_object_poll");
+       RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+       RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
+
+       prop = RNA_def_property(srna, "falloff", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 2.0f, 16.0f);
+       RNA_def_property_ui_text(prop, "Interpolation falloff", "Controls how much nearby polygons influence deformation");
+       RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+       prop = RNA_def_property(srna, "is_bound", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_funcs(prop, "rna_SurfaceDeformModifier_is_bound_get", NULL);
+       RNA_def_property_ui_text(prop, "Bound", "Whether geometry has been bound to target mesh");
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+}
+
 void RNA_def_modifier(BlenderRNA *brna)
 {
        StructRNA *srna;
@@ -4819,6 +4855,7 @@ void RNA_def_modifier(BlenderRNA *brna)
        rna_def_modifier_datatransfer(brna);
        rna_def_modifier_normaledit(brna);
        rna_def_modifier_meshseqcache(brna);
+       rna_def_modifier_surfacedeform(brna);
 }
 
 #endif
index bacfc177432a60e515142995f766ba6f0e14134c..ad2b862141cd807421ab807a57bd4ab7d3016696 100644 (file)
@@ -93,6 +93,7 @@ set(SRC
        intern/MOD_solidify.c
        intern/MOD_subsurf.c
        intern/MOD_surface.c
+       intern/MOD_surfacedeform.c
        intern/MOD_triangulate.c
        intern/MOD_util.c
        intern/MOD_uvwarp.c
index 4c88144589350cd5c5c6bb39fba589269d97bc58..bf121af2bd143b91fa6fb9deb49b4416352a5cf4 100644 (file)
@@ -85,6 +85,7 @@ extern ModifierTypeInfo modifierType_DataTransfer;
 extern ModifierTypeInfo modifierType_NormalEdit;
 extern ModifierTypeInfo modifierType_CorrectiveSmooth;
 extern ModifierTypeInfo modifierType_MeshSequenceCache;
+extern ModifierTypeInfo modifierType_SurfaceDeform;
 
 /* MOD_util.c */
 void modifier_type_init(ModifierTypeInfo *types[]);
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
new file mode 100644 (file)
index 0000000..5e852e8
--- /dev/null
@@ -0,0 +1,1189 @@
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_alloca.h"
+#include "BLI_math.h"
+#include "BLI_math_geom.h"
+#include "BLI_task.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_editmesh.h"
+#include "BKE_library_query.h"
+#include "BKE_modifier.h"
+
+#include "depsgraph_private.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "MOD_util.h"
+
+typedef struct SDefAdjacency {
+       struct SDefAdjacency *next;
+       unsigned int index;
+} SDefAdjacency;
+
+typedef struct SDefAdjacencyArray {
+       SDefAdjacency *first;
+       unsigned int num; /* Careful, this is twice the number of polygons (avoids an extra loop) */
+} SDefAdjacencyArray;
+
+typedef struct SDefEdgePolys {
+       unsigned int polys[2], num;
+} SDefEdgePolys;
+
+typedef struct SDefBindCalcData {
+       BVHTreeFromMesh * const treeData;
+       const SDefAdjacencyArray * const vert_edges;
+       const SDefEdgePolys * const edge_polys;
+       SDefVert * const bind_verts;
+       const MLoopTri * const looptri;
+       const MPoly * const mpoly;
+       const MEdge * const medge;
+       const MLoop * const mloop;
+       const MVert * const mvert;
+       float (* const vertexCos)[3];
+       const float falloff;
+       int success;
+} SDefBindCalcData;
+
+typedef struct SDefBindPoly {
+       float (*coords)[3];
+       float (*coords_v2)[2];
+       float point_v2[2];
+       float weight_angular;
+       float weight_dist_proj;
+       float weight_dist;
+       float weight;
+       float scales[2];
+       float centroid[3];
+       float centroid_v2[2];
+       float normal[3];
+       float cent_edgemid_vecs_v2[2][2];
+       float edgemid_angle;
+       float point_edgemid_angles[2];
+       float corner_edgemid_angles[2];
+       float dominant_angle_weight;
+       unsigned int index;
+       unsigned int numverts;
+       unsigned int loopstart;
+       unsigned int edge_inds[2];
+       unsigned int edge_vert_inds[2];
+       unsigned int corner_ind;
+       unsigned int dominant_edge;
+       bool inside;
+} SDefBindPoly;
+
+typedef struct SDefBindWeightData {
+       SDefBindPoly *bind_polys;
+       unsigned int numpoly;
+       unsigned int numbinds;
+} SDefBindWeightData;
+
+typedef struct SDefDeformData {
+       const SDefVert * const bind_verts;
+       const MVert * const mvert;
+       float (* const vertexCos)[3];
+} SDefDeformData;
+
+/* Bind result values */
+enum {
+       MOD_SDEF_BIND_RESULT_SUCCESS = 1,
+       MOD_SDEF_BIND_RESULT_GENERIC_ERR = 0,
+       MOD_SDEF_BIND_RESULT_MEM_ERR = -1,
+       MOD_SDEF_BIND_RESULT_NONMANY_ERR = -2,
+       MOD_SDEF_BIND_RESULT_CONCAVE_ERR = -3,
+       MOD_SDEF_BIND_RESULT_OVERLAP_ERR = -4,
+};
+
+/* Infinite weight flags */
+enum {
+       MOD_SDEF_INFINITE_WEIGHT_ANGULAR = (1 << 0),
+       MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ = (1 << 1),
+       MOD_SDEF_INFINITE_WEIGHT_DIST = (1 << 2),
+};
+
+static void initData(ModifierData *md)
+{
+       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+       smd->target = NULL;
+       smd->verts = NULL;
+       smd->flags = 0;
+       smd->falloff = 4.0f;
+}
+
+static void freeData(ModifierData *md)
+{
+       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+
+       if (smd->verts) {
+               for (int i = 0; i < smd->numverts; i++) {
+                       if (smd->verts[i].binds) {
+                               for (int j = 0; j < smd->verts[i].numbinds; j++) {
+                                       MEM_SAFE_FREE(smd->verts[i].binds[j].vert_inds);
+                                       MEM_SAFE_FREE(smd->verts[i].binds[j].vert_weights);
+                               }
+
+                               MEM_freeN(smd->verts[i].binds);
+                       }
+               }
+
+               MEM_freeN(smd->verts);
+               smd->verts = NULL;
+       }
+}
+
+static void copyData(ModifierData *md, ModifierData *target)
+{
+       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+       SurfaceDeformModifierData *tsmd = (SurfaceDeformModifierData *)target;
+
+       *tsmd = *smd;
+
+       if (smd->verts) {
+               tsmd->verts = MEM_dupallocN(smd->verts);
+
+               for (int i = 0; i < smd->numverts; i++) {
+                       if (smd->verts[i].binds) {
+                               tsmd->verts[i].binds = MEM_dupallocN(smd->verts[i].binds);
+
+                               for (int j = 0; j < smd->verts[i].numbinds; j++) {
+                                       if (smd->verts[i].binds[j].vert_inds) {
+                                               tsmd->verts[i].binds[j].vert_inds = MEM_dupallocN(smd->verts[i].binds[j].vert_inds);
+                                       }
+
+                                       if (smd->verts[i].binds[j].vert_weights) {
+                                               tsmd->verts[i].binds[j].vert_weights = MEM_dupallocN(smd->verts[i].binds[j].vert_weights);
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData)
+{
+       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+
+       walk(userData, ob, &smd->target, IDWALK_NOP);
+}
+
+static void updateDepgraph(ModifierData *md, DagForest *forest,
+                           struct Main *UNUSED(bmain),
+                           struct Scene *UNUSED(scene),
+                           Object *UNUSED(ob),
+                           DagNode *obNode)
+{
+       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+
+       if (smd->target) {
+               DagNode *curNode = dag_get_node(forest, smd->target);
+
+               dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA, "Surface Deform Modifier");
+       }
+}
+
+static void updateDepsgraph(ModifierData *md,
+                            struct Main *UNUSED(bmain),
+                            struct Scene *UNUSED(scene),
+                            Object *UNUSED(ob),
+                            struct DepsNodeHandle *node)
+{
+       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+       if (smd->target != NULL) {
+               DEG_add_object_relation(node, smd->target, DEG_OB_COMP_GEOMETRY, "Surface Deform Modifier");
+       }
+}
+
+static void freeAdjacencyMap(SDefAdjacencyArray * const vert_edges, SDefAdjacency * const adj_ref, SDefEdgePolys * const edge_polys)
+{
+       MEM_freeN(edge_polys);
+
+       MEM_freeN(adj_ref);
+
+       MEM_freeN(vert_edges);
+}
+
+static int buildAdjacencyMap(const MPoly *poly, const MEdge *edge, const MLoop * const mloop, const unsigned int numpoly, const unsigned int numedges,
+                              SDefAdjacencyArray * const vert_edges, SDefAdjacency *adj, SDefEdgePolys * const edge_polys)
+{
+       const MLoop *loop;
+
+       /* Fing polygons adjacent to edges */
+       for (int i = 0; i < numpoly; i++, poly++) {
+               loop = &mloop[poly->loopstart];
+
+               for (int j = 0; j < poly->totloop; j++, loop++) {
+                       if (edge_polys[loop->e].num == 0) {
+                               edge_polys[loop->e].polys[0] = i;
+                               edge_polys[loop->e].polys[1] = -1;
+                               edge_polys[loop->e].num++;
+                       }
+                       else if (edge_polys[loop->e].num == 1) {
+                               edge_polys[loop->e].polys[1] = i;
+                               edge_polys[loop->e].num++;
+                       }
+                       else {
+                               return MOD_SDEF_BIND_RESULT_NONMANY_ERR;
+                       }
+               }
+       }
+
+       /* Find edges adjacent to vertices */
+       for (int i = 0; i < numedges; i++, edge++) {
+               adj->next = vert_edges[edge->v1].first;
+               adj->index = i;
+               vert_edges[edge->v1].first = adj;
+               vert_edges[edge->v1].num += edge_polys[i].num;
+               adj++;
+
+               adj->next = vert_edges[edge->v2].first;
+               adj->index = i;
+               vert_edges[edge->v2].first = adj;
+               vert_edges[edge->v2].num += edge_polys[i].num;
+               adj++;
+       }
+
+       return MOD_SDEF_BIND_RESULT_SUCCESS;
+}
+
+BLI_INLINE void sortPolyVertsEdge(unsigned int *indices, const MLoop * const mloop, const unsigned int edge, const unsigned int num)
+{
+       bool found = false;
+
+       for (int i = 0; i < num; i++) {
+               if (mloop[i].e == edge) {
+                       found = true;
+               }
+               if (found) {
+                       *indices = mloop[i].v;
+                       indices++;
+               }
+       }
+
+       /* Fill in remaining vertex indices that occur before the edge */
+       for (int i = 0; mloop[i].e != edge; i++) {
+               *indices = mloop[i].v;
+               indices++;
+       }
+}
+
+BLI_INLINE void sortPolyVertsTri(unsigned int *indices, const MLoop * const mloop, const unsigned int loopstart, const unsigned int num)
+{
+       for (int i = loopstart; i < num; i++) {
+               *indices = mloop[i].v;
+               indices++;
+       }
+
+       for (int i = 0; i < loopstart; i++) {
+               *indices = mloop[i].v;
+               indices++;
+       }
+}
+
+BLI_INLINE unsigned int nearestVert(SDefBindCalcData * const data, const float point_co[3])
+{
+       const MVert * const mvert = data->mvert;
+       BVHTreeNearest nearest = {.dist_sq = FLT_MAX, .index = -1};
+       const MPoly *poly;
+       const MEdge *edge;
+       const MLoop *loop;
+       float max_dist = FLT_MAX;
+       float dist;
+       unsigned int index = 0;
+
+       BLI_bvhtree_find_nearest(data->treeData->tree, point_co, &nearest, data->treeData->nearest_callback, data->treeData);
+
+       poly = &data->mpoly[data->looptri[nearest.index].poly];
+       loop = &data->mloop[poly->loopstart];
+
+       for (int i = 0; i < poly->totloop; i++, loop++) {
+               edge = &data->medge[loop->e];
+               dist = dist_squared_to_line_segment_v3(point_co, mvert[edge->v1].co, mvert[edge->v2].co);
+
+               if (dist < max_dist) {
+                       max_dist = dist;
+                       index = loop->e;
+               }
+       }
+
+       edge = &data->medge[index];
+       if (len_squared_v3v3(point_co, mvert[edge->v1].co) < len_squared_v3v3(point_co, mvert[edge->v2].co)) {
+               return edge->v1;
+       }
+       else {
+               return edge->v2;
+       }
+}
+
+BLI_INLINE int isPolyValid(const float coords[][2], const unsigned int nr)
+{
+       float prev_co[2];
+       float curr_vec[2], prev_vec[2];
+
+       if (!is_poly_convex_v2(coords, nr)) {
+               return MOD_SDEF_BIND_RESULT_CONCAVE_ERR;
+       }
+
+       copy_v2_v2(prev_co, coords[nr - 1]);
+       sub_v2_v2v2(prev_vec, prev_co, coords[nr - 2]);
+
+       for (int i = 0; i < nr; i++) {
+               sub_v2_v2v2(curr_vec, coords[i], prev_co);
+
+               if (len_squared_v2(curr_vec) < FLT_EPSILON) {
+                       return MOD_SDEF_BIND_RESULT_OVERLAP_ERR;
+               }
+
+               if (1.0f - dot_v2v2(prev_vec, curr_vec) < FLT_EPSILON) {
+                       return MOD_SDEF_BIND_RESULT_CONCAVE_ERR;
+               }
+
+               copy_v2_v2(prev_co, coords[i]);
+               copy_v2_v2(prev_vec, curr_vec);
+       }
+
+       return MOD_SDEF_BIND_RESULT_SUCCESS;
+}
+
+static void freeBindData(SDefBindWeightData * const bwdata)
+{
+       SDefBindPoly *bpoly = bwdata->bind_polys;
+
+       if (bwdata->bind_polys) {
+               for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
+                       MEM_SAFE_FREE(bpoly->coords);
+                       MEM_SAFE_FREE(bpoly->coords_v2);
+               }
+
+               MEM_freeN(bwdata->bind_polys);
+       }
+
+       MEM_freeN(bwdata);
+}
+
+BLI_INLINE float computeAngularWeight(const float point_angle, const float edgemid_angle)
+{
+       float weight;
+
+       weight = point_angle;
+       weight /= edgemid_angle;
+       weight *= M_PI_2;
+
+       return sinf(weight);
+}
+
+BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData * const data, const float point_co[3])
+{
+       const unsigned int nearest = nearestVert(data, point_co);
+       const SDefAdjacency * const vert_edges = data->vert_edges[nearest].first;
+       const SDefEdgePolys * const edge_polys = data->edge_polys;
+
+       const SDefAdjacency *vedge;
+       const MPoly *poly;
+       const MLoop *loop;
+
+       SDefBindWeightData *bwdata;
+       SDefBindPoly *bpoly;
+
+       float world[3] = {0.0f, 0.0f, 1.0f};
+       float avg_point_dist = 0.0f;
+       float tot_weight = 0.0f;
+       int inf_weight_flags = 0;
+
+       bwdata = MEM_callocN(sizeof(*bwdata), "SDefBindWeightData");
+       if (bwdata == NULL) {
+               data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+               return NULL;
+       }
+
+       bwdata->numpoly = data->vert_edges[nearest].num / 2;
+
+       bpoly = MEM_callocN(sizeof(*bpoly) * bwdata->numpoly, "SDefBindPoly");
+       if (bpoly == NULL) {
+               freeBindData(bwdata);
+               data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+               return NULL;
+       }
+
+       bwdata->bind_polys = bpoly;
+
+       /* Loop over all adjacent edges, and build the SDefBindPoly data for each poly adjacent to those */
+       for (vedge = vert_edges; vedge; vedge = vedge->next) {
+               unsigned int edge_ind = vedge->index;
+
+               for (int i = 0; i < edge_polys[edge_ind].num; i++) {
+                       {
+                               bpoly = bwdata->bind_polys;
+
+                               for (int j = 0; j < bwdata->numpoly; bpoly++, j++) {
+                                       /* If coords isn't allocated, we have reached the first uninitialized bpoly */
+                                       if ((bpoly->index == edge_polys[edge_ind].polys[i]) || (!bpoly->coords)) {
+                                               break;
+                                       }
+                               }
+                       }
+
+                       /* Check if poly was already created by another edge or still has to be initialized */
+                       if (!bpoly->coords) {
+                               float angle;
+                               float axis[3];
+                               float tmp_vec_v2[2];
+                               int is_poly_valid;
+
+                               bpoly->index = edge_polys[edge_ind].polys[i];
+                               bpoly->coords = NULL;
+                               bpoly->coords_v2 = NULL;
+
+                               /* Copy poly data */
+                               poly = &data->mpoly[bpoly->index];
+                               loop = &data->mloop[poly->loopstart];
+
+                               bpoly->numverts = poly->totloop;
+                               bpoly->loopstart = poly->loopstart;
+
+                               bpoly->coords = MEM_mallocN(sizeof(*bpoly->coords) * poly->totloop, "SDefBindPolyCoords");
+                               if (bpoly->coords == NULL) {
+                                       freeBindData(bwdata);
+                                       data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+                                       return NULL;
+                               }
+
+                               bpoly->coords_v2 = MEM_mallocN(sizeof(*bpoly->coords_v2) * poly->totloop, "SDefBindPolyCoords_v2");
+                               if (bpoly->coords_v2 == NULL) {
+                                       freeBindData(bwdata);
+                                       data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+                                       return NULL;
+                               }
+
+                               for (int j = 0; j < poly->totloop; j++, loop++) {
+                                       copy_v3_v3(bpoly->coords[j], data->mvert[loop->v].co);
+
+                                       /* Find corner and edge indices within poly loop array */
+                                       if (loop->v == nearest) {
+                                               bpoly->corner_ind = j;
+                                               bpoly->edge_vert_inds[0] = (j == 0) ? (poly->totloop - 1) : (j - 1);
+                                               bpoly->edge_vert_inds[1] = (j == poly->totloop - 1) ? (0) : (j + 1);
+
+                                               bpoly->edge_inds[0] = data->mloop[poly->loopstart + bpoly->edge_vert_inds[0]].e;
+                                               bpoly->edge_inds[1] = loop->e;
+                                       }
+                               }
+
+                               /* Compute poly's parametric data */
+                               mid_v3_v3_array(bpoly->centroid, bpoly->coords, poly->totloop);
+                               normal_poly_v3(bpoly->normal, bpoly->coords, poly->totloop);
+
+                               /* Compute poly skew angle and axis */
+                               angle = angle_normalized_v3v3(bpoly->normal, world);
+
+                               cross_v3_v3v3(axis, bpoly->normal, world);
+                               normalize_v3(axis);
+
+                               /* Map coords onto 2d normal plane */
+                               map_to_plane_axis_angle_v2_v3v3fl(bpoly->point_v2, point_co, axis, angle);
+
+                               zero_v2(bpoly->centroid_v2);
+                               for (int j = 0; j < poly->totloop; j++) {
+                                       map_to_plane_axis_angle_v2_v3v3fl(bpoly->coords_v2[j], bpoly->coords[j], axis, angle);
+                                       madd_v2_v2fl(bpoly->centroid_v2, bpoly->coords_v2[j], 1.0f / poly->totloop);
+                               }
+
+                               is_poly_valid = isPolyValid(bpoly->coords_v2, poly->totloop);
+
+                               if (is_poly_valid != MOD_SDEF_BIND_RESULT_SUCCESS) {
+                                       freeBindData(bwdata);
+                                       data->success = is_poly_valid;
+                                       return NULL;
+                               }
+
+                               bpoly->inside = isect_point_poly_v2(bpoly->point_v2, bpoly->coords_v2, poly->totloop, false);
+
+                               /* Initialize weight components */
+                               bpoly->weight_angular = 1.0f;
+                               bpoly->weight_dist_proj = len_v2v2(bpoly->centroid_v2, bpoly->point_v2);
+                               bpoly->weight_dist = len_v3v3(bpoly->centroid, point_co);
+
+                               avg_point_dist += bpoly->weight_dist;
+
+                               /* Compute centroid to mid-edge vectors */
+                               mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[0],
+                                           bpoly->coords_v2[bpoly->edge_vert_inds[0]],
+                                           bpoly->coords_v2[bpoly->corner_ind]);
+
+                               mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[1],
+                                           bpoly->coords_v2[bpoly->edge_vert_inds[1]],
+                                           bpoly->coords_v2[bpoly->corner_ind]);
+
+                               sub_v2_v2(bpoly->cent_edgemid_vecs_v2[0], bpoly->centroid_v2);
+                               sub_v2_v2(bpoly->cent_edgemid_vecs_v2[1], bpoly->centroid_v2);
+
+                               /* Compute poly scales with respect to mid-edges, and normalize the vectors */
+                               bpoly->scales[0] = normalize_v2(bpoly->cent_edgemid_vecs_v2[0]);
+                               bpoly->scales[1] = normalize_v2(bpoly->cent_edgemid_vecs_v2[1]);
+
+                               /* Compute the required polygon angles */
+                               bpoly->edgemid_angle = angle_normalized_v2v2(bpoly->cent_edgemid_vecs_v2[0], bpoly->cent_edgemid_vecs_v2[1]);
+
+                               sub_v2_v2v2(tmp_vec_v2, bpoly->coords_v2[bpoly->corner_ind], bpoly->centroid_v2);
+                               normalize_v2(tmp_vec_v2);
+
+                               bpoly->corner_edgemid_angles[0] = angle_normalized_v2v2(tmp_vec_v2, bpoly->cent_edgemid_vecs_v2[0]);
+                               bpoly->corner_edgemid_angles[1] = angle_normalized_v2v2(tmp_vec_v2, bpoly->cent_edgemid_vecs_v2[1]);
+
+                               /* Check for inifnite weights, and compute angular data otherwise */
+                               if (bpoly->weight_dist < FLT_EPSILON) {
+                                       inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ;
+                                       inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST;
+                               }
+                               else if (bpoly->weight_dist_proj < FLT_EPSILON) {
+                                       inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ;
+                               }
+                               else {
+                                       float cent_point_vec[2];
+
+                                       sub_v2_v2v2(cent_point_vec, bpoly->point_v2, bpoly->centroid_v2);
+                                       normalize_v2(cent_point_vec);
+
+                                       bpoly->point_edgemid_angles[0] = angle_normalized_v2v2(cent_point_vec, bpoly->cent_edgemid_vecs_v2[0]);
+                                       bpoly->point_edgemid_angles[1] = angle_normalized_v2v2(cent_point_vec, bpoly->cent_edgemid_vecs_v2[1]);
+                               }
+                       }
+               }
+       }
+
+       avg_point_dist /= bwdata->numpoly;
+
+       /* If weights 1 and 2 are not infinite, loop over all adjacent edges again,
+        * and build adjacency dependent angle data (depends on all polygons having been computed) */
+       if (!inf_weight_flags) {
+               for (vedge = vert_edges; vedge; vedge = vedge->next) {
+                       SDefBindPoly *bpolys[2];
+                       const SDefEdgePolys *epolys;
+                       float ang_weights[2];
+                       unsigned int edge_ind = vedge->index;
+                       unsigned int edge_on_poly[2];
+
+                       epolys = &edge_polys[edge_ind];
+
+                       /* Find bind polys corresponding to the edge's adjacent polys */
+                       bpoly = bwdata->bind_polys;
+
+                       for (int i = 0, j = 0; (i < bwdata->numpoly) && (j < epolys->num); bpoly++, i++) {
+                               if (ELEM(bpoly->index, epolys->polys[0], epolys->polys[1])) {
+                                       bpolys[j] = bpoly;
+
+                                       if (bpoly->edge_inds[0] == edge_ind) {
+                                               edge_on_poly[j] = 0;
+                                       }
+                                       else {
+                                               edge_on_poly[j] = 1;
+                                       }
+
+                                       j++;
+                               }
+                       }
+
+                       /* Compute angular weight component */
+                       if (epolys->num == 1) {
+                               ang_weights[0] = computeAngularWeight(bpolys[0]->point_edgemid_angles[edge_on_poly[0]], bpolys[0]->edgemid_angle);
+                               bpolys[0]->weight_angular *= ang_weights[0] * ang_weights[0];
+                       }
+                       else if (epolys->num == 2) {
+                               ang_weights[0] = computeAngularWeight(bpolys[0]->point_edgemid_angles[edge_on_poly[0]], bpolys[0]->edgemid_angle);
+                               ang_weights[1] = computeAngularWeight(bpolys[1]->point_edgemid_angles[edge_on_poly[1]], bpolys[1]->edgemid_angle);
+
+                               bpolys[0]->weight_angular *= ang_weights[0] * ang_weights[1];
+                               bpolys[1]->weight_angular *= ang_weights[0] * ang_weights[1];
+                       }
+               }
+       }
+
+       /* Compute scalings and falloff.
+        * Scale all weights if no infinite weight is found,
+        * scale only unprojected weight if projected weight is infinite,
+        * scale none if both are infinite. */
+       if (!inf_weight_flags) {
+               bpoly = bwdata->bind_polys;
+
+               for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
+                       float corner_angle_weights[2];
+                       float scale_weight, sqr, inv_sqr;
+
+                       corner_angle_weights[0] = bpoly->point_edgemid_angles[0] / bpoly->corner_edgemid_angles[0];
+                       corner_angle_weights[1] = bpoly->point_edgemid_angles[1] / bpoly->corner_edgemid_angles[1];
+
+                       if (isnan(corner_angle_weights[0]) || isnan(corner_angle_weights[1])) {
+                               freeBindData(bwdata);
+                               data->success = MOD_SDEF_BIND_RESULT_GENERIC_ERR;
+                               return NULL;
+                       }
+
+                       /* Find which edge the point is closer to */
+                       if (corner_angle_weights[0] < corner_angle_weights[1]) {
+                               bpoly->dominant_edge = 0;
+                               bpoly->dominant_angle_weight = corner_angle_weights[0];
+                       }
+                       else {
+                               bpoly->dominant_edge = 1;
+                               bpoly->dominant_angle_weight = corner_angle_weights[1];
+                       }
+
+                       bpoly->dominant_angle_weight = sinf(bpoly->dominant_angle_weight * M_PI_2);
+
+                       /* Compute quadratic angular scale interpolation weight */
+                       scale_weight = bpoly->point_edgemid_angles[bpoly->dominant_edge] / bpoly->edgemid_angle;
+                       scale_weight /= scale_weight + (bpoly->point_edgemid_angles[!bpoly->dominant_edge] / bpoly->edgemid_angle);
+
+                       sqr = scale_weight * scale_weight;
+                       inv_sqr = 1.0f - scale_weight;
+                       inv_sqr *= inv_sqr;
+                       scale_weight = sqr / (sqr + inv_sqr);
+
+                       /* Compute interpolated scale (no longer need the individual scales,
+                        * so simply storing the result over the scale in index zero) */
+                       bpoly->scales[0] = bpoly->scales[bpoly->dominant_edge] * (1.0f - scale_weight) +
+                                          bpoly->scales[!bpoly->dominant_edge] * scale_weight;
+
+                       /* Scale the point distance weights, and introduce falloff */
+                       bpoly->weight_dist_proj /= bpoly->scales[0];
+                       bpoly->weight_dist_proj = powf(bpoly->weight_dist_proj, data->falloff);
+
+                       bpoly->weight_dist /= avg_point_dist;
+                       bpoly->weight_dist = powf(bpoly->weight_dist, data->falloff);
+
+                       /* Re-check for infinite weights, now that all scalings and interpolations are computed */
+                       if (bpoly->weight_dist < FLT_EPSILON) {
+                               inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ;
+                               inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST;
+                       }
+                       else if (bpoly->weight_dist_proj < FLT_EPSILON) {
+                               inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ;
+                       }
+                       else if (bpoly->weight_angular < FLT_EPSILON) {
+                               inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_ANGULAR;
+                       }
+               }
+       }
+       else if (!(inf_weight_flags & MOD_SDEF_INFINITE_WEIGHT_DIST)) {
+               bpoly = bwdata->bind_polys;
+
+               for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
+                       /* Scale the point distance weight by average point distance, and introduce falloff */
+                       bpoly->weight_dist /= avg_point_dist;
+                       bpoly->weight_dist = powf(bpoly->weight_dist, data->falloff);
+
+                       /* Re-check for infinite weights, now that all scalings and interpolations are computed */
+                       if (bpoly->weight_dist < FLT_EPSILON) {
+                               inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST;
+                       }
+               }
+       }
+
+       /* Final loop, to compute actual weights */
+       bpoly = bwdata->bind_polys;
+
+       for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
+               /* Weight computation from components */
+               if (inf_weight_flags & MOD_SDEF_INFINITE_WEIGHT_DIST) {
+                       bpoly->weight = bpoly->weight_dist < FLT_EPSILON ? 1.0f : 0.0f;
+               }
+               else if (inf_weight_flags & MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ) {
+                       bpoly->weight = bpoly->weight_dist_proj < FLT_EPSILON ?
+                                       1.0f / bpoly->weight_dist : 0.0f;
+               }
+               else if (inf_weight_flags & MOD_SDEF_INFINITE_WEIGHT_ANGULAR) {
+                       bpoly->weight = bpoly->weight_angular < FLT_EPSILON ?
+                                       1.0f / bpoly->weight_dist_proj / bpoly->weight_dist : 0.0f;
+               }
+               else {
+                       bpoly->weight = 1.0f / bpoly->weight_angular /
+                                              bpoly->weight_dist_proj /
+                                              bpoly->weight_dist;
+               }
+
+               tot_weight += bpoly->weight;
+       }
+
+       bpoly = bwdata->bind_polys;
+
+       for (int i = 0; i < bwdata->numpoly; bpoly++, i++) {
+               bpoly->weight /= tot_weight;
+
+               /* Evaluate if this poly is relevant to bind */
+               /* Even though the weights should add up to 1.0,
+                * the losses of weights smaller than epsilon here
+                * should be negligible... */
+               if (bpoly->weight >= FLT_EPSILON) {
+                       if (bpoly->inside) {
+                               bwdata->numbinds += 1;
+                       }
+                       else {
+                               if (bpoly->dominant_angle_weight < FLT_EPSILON || 1.0f - bpoly->dominant_angle_weight < FLT_EPSILON) {
+                                       bwdata->numbinds += 1;
+                               }
+                               else {
+                                       bwdata->numbinds += 2;
+                               }
+                       }
+               }
+       }
+
+       return bwdata;
+}
+
+BLI_INLINE float computeNormalDisplacement(const float point_co[3], const float point_co_proj[3], const float normal[3])
+{
+       float disp_vec[3];
+       float normal_dist;
+
+       sub_v3_v3v3(disp_vec, point_co, point_co_proj);
+       normal_dist = len_v3(disp_vec);
+
+       if (dot_v3v3(disp_vec, normal) < 0) {
+               normal_dist *= -1;
+       }
+
+       return normal_dist;
+}
+
+static void bindVert(void *userdata, void *UNUSED(userdata_chunk), const int index, const int UNUSED(threadid))
+{
+       SDefBindCalcData * const data = (SDefBindCalcData *)userdata;
+       float point_co[3];
+       float point_co_proj[3];
+
+       SDefBindWeightData *bwdata;
+       SDefVert *sdvert = data->bind_verts + index;
+       SDefBindPoly *bpoly;
+       SDefBind *sdbind;
+
+       if (data->success != MOD_SDEF_BIND_RESULT_SUCCESS) {
+               sdvert->binds = NULL;
+               sdvert->numbinds = 0;
+               return;
+       }
+
+       copy_v3_v3(point_co, data->vertexCos[index]);
+       bwdata = computeBindWeights(data, point_co);
+
+       if (bwdata == NULL) {
+               sdvert->binds = NULL;
+               sdvert->numbinds = 0;
+               return;
+       }
+
+       sdvert->binds = MEM_callocN(sizeof(*sdvert->binds) * bwdata->numbinds, "SDefVertBindData");
+       if (sdvert->binds == NULL) {
+               data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+               sdvert->numbinds = 0;
+               return;
+       }
+
+       sdvert->numbinds = bwdata->numbinds;
+
+       sdbind = sdvert->binds;
+
+       bpoly = bwdata->bind_polys;
+
+       for (int i = 0; i < bwdata->numbinds; bpoly++) {
+               if (bpoly->weight >= FLT_EPSILON) {
+                       if (bpoly->inside) {
+                               const MLoop *loop = &data->mloop[bpoly->loopstart];
+
+                               sdbind->influence = bpoly->weight;
+                               sdbind->numverts = bpoly->numverts;
+
+                               sdbind->mode = MOD_SDEF_MODE_NGON;
+                               sdbind->vert_weights = MEM_mallocN(sizeof(*sdbind->vert_weights) * bpoly->numverts, "SDefNgonVertWeights");
+                               if (sdbind->vert_weights == NULL) {
+                                       data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+                                       return;
+                               }
+
+                               sdbind->vert_inds = MEM_mallocN(sizeof(*sdbind->vert_inds) * bpoly->numverts, "SDefNgonVertInds");
+                               if (sdbind->vert_inds == NULL) {
+                                       data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+                                       return;
+                               }
+
+                               interp_weights_poly_v2(sdbind->vert_weights, bpoly->coords_v2, bpoly->numverts, bpoly->point_v2);
+
+                               /* Reproject vert based on weights and original poly verts, to reintroduce poly non-planarity */
+                               zero_v3(point_co_proj);
+                               for (int j = 0; j < bpoly->numverts; j++, loop++) {
+                                       madd_v3_v3fl(point_co_proj, bpoly->coords[j], sdbind->vert_weights[j]);
+                                       sdbind->vert_inds[j] = loop->v;
+                               }
+
+                               sdbind->normal_dist = computeNormalDisplacement(point_co, point_co_proj, bpoly->normal);
+
+                               sdbind++;
+                               i++;
+                       }
+                       else {
+                               float tmp_vec[3];
+                               float cent[3], norm[3];
+                               float v1[3], v2[3], v3[3];
+
+                               if (1.0f - bpoly->dominant_angle_weight >= FLT_EPSILON) {
+                                       sdbind->influence = bpoly->weight * (1.0f - bpoly->dominant_angle_weight);
+                                       sdbind->numverts = bpoly->numverts;
+
+                                       sdbind->mode = MOD_SDEF_MODE_CENTROID;
+                                       sdbind->vert_weights = MEM_mallocN(sizeof(*sdbind->vert_weights) * 3, "SDefCentVertWeights");
+                                       if (sdbind->vert_weights == NULL) {
+                                               data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+                                               return;
+                                       }
+
+                                       sdbind->vert_inds = MEM_mallocN(sizeof(*sdbind->vert_inds) * bpoly->numverts, "SDefCentVertInds");
+                                       if (sdbind->vert_inds == NULL) {
+                                               data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+                                               return;
+                                       }
+
+                                       sortPolyVertsEdge(sdbind->vert_inds, &data->mloop[bpoly->loopstart],
+                                                         bpoly->edge_inds[bpoly->dominant_edge], bpoly->numverts);
+
+                                       copy_v3_v3(v1, data->mvert[sdbind->vert_inds[0]].co);
+                                       copy_v3_v3(v2, data->mvert[sdbind->vert_inds[1]].co);
+                                       copy_v3_v3(v3, bpoly->centroid);
+
+                                       mid_v3_v3v3v3(cent, v1, v2, v3);
+                                       normal_tri_v3(norm, v1, v2, v3);
+
+                                       add_v3_v3v3(tmp_vec, point_co, bpoly->normal);
+
+                                       /* We are sure the line is not parallel to the plane.
+                                        * Checking return value just to avoid warning... */
+                                       if (!isect_line_plane_v3(point_co_proj, point_co, tmp_vec, cent, norm)) {
+                                               BLI_assert(false);
+                                       }
+
+                                       interp_weights_tri_v3(sdbind->vert_weights, v1, v2, v3, point_co_proj);
+
+                                       sdbind->normal_dist = computeNormalDisplacement(point_co, point_co_proj, bpoly->normal);
+
+                                       sdbind++;
+                                       i++;
+                               }
+
+                               if (bpoly->dominant_angle_weight >= FLT_EPSILON) {
+                                       sdbind->influence = bpoly->weight * bpoly->dominant_angle_weight;
+                                       sdbind->numverts = bpoly->numverts;
+
+                                       sdbind->mode = MOD_SDEF_MODE_LOOPTRI;
+                                       sdbind->vert_weights = MEM_mallocN(sizeof(*sdbind->vert_weights) * 3, "SDefTriVertWeights");
+                                       if (sdbind->vert_weights == NULL) {
+                                               data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+                                               return;
+                                       }
+
+                                       sdbind->vert_inds = MEM_mallocN(sizeof(*sdbind->vert_inds) * bpoly->numverts, "SDefTriVertInds");
+                                       if (sdbind->vert_inds == NULL) {
+                                               data->success = MOD_SDEF_BIND_RESULT_MEM_ERR;
+                                               return;
+                                       }
+
+                                       sortPolyVertsTri(sdbind->vert_inds, &data->mloop[bpoly->loopstart], bpoly->edge_vert_inds[0], bpoly->numverts);
+
+                                       copy_v3_v3(v1, data->mvert[sdbind->vert_inds[0]].co);
+                                       copy_v3_v3(v2, data->mvert[sdbind->vert_inds[1]].co);
+                                       copy_v3_v3(v3, data->mvert[sdbind->vert_inds[2]].co);
+
+                                       mid_v3_v3v3v3(cent, v1, v2, v3);
+                                       normal_tri_v3(norm, v1, v2, v3);
+
+                                       add_v3_v3v3(tmp_vec, point_co, bpoly->normal);
+
+                                       /* We are sure the line is not parallel to the plane.
+                                        * Checking return value just to avoid warning... */
+                                       if (!isect_line_plane_v3(point_co_proj, point_co, tmp_vec, cent, norm)) {
+                                               BLI_assert(false);
+                                       }
+
+                                       interp_weights_tri_v3(sdbind->vert_weights, v1, v2, v3, point_co_proj);
+
+                                       sdbind->normal_dist = computeNormalDisplacement(point_co, point_co_proj, bpoly->normal);
+
+                                       sdbind++;
+                                       i++;
+                               }
+                       }
+               }
+       }
+
+       freeBindData(bwdata);
+}
+
+static bool surfacedeformBind(SurfaceDeformModifierData *smd, float (*vertexCos)[3],
+                              unsigned int numverts, unsigned int tnumpoly, DerivedMesh *tdm)
+{
+       BVHTreeFromMesh treeData = {NULL};
+       const MPoly *mpoly = tdm->getPolyArray(tdm);
+       const MEdge *medge = tdm->getEdgeArray(tdm);
+       const MLoop *mloop = tdm->getLoopArray(tdm);
+       unsigned int tnumedges = tdm->getNumEdges(tdm);
+       unsigned int tnumverts = tdm->getNumVerts(tdm);
+       int adj_result;
+       SDefAdjacencyArray *vert_edges;
+       SDefAdjacency *adj_array;
+       SDefEdgePolys *edge_polys;
+
+       vert_edges = MEM_callocN(sizeof(*vert_edges) * tnumverts, "SDefVertEdgeMap");
+       if (vert_edges == NULL) {
+               modifier_setError((ModifierData *)smd, "Out of memory");
+               return false;
+       }
+
+       adj_array = MEM_mallocN(sizeof(*adj_array) * tnumedges * 2, "SDefVertEdge");
+       if (adj_array == NULL) {
+               modifier_setError((ModifierData *)smd, "Out of memory");
+               MEM_freeN(vert_edges);
+               return false;
+       }
+
+       edge_polys = MEM_callocN(sizeof(*edge_polys) * tnumedges, "SDefEdgeFaceMap");
+       if (edge_polys == NULL) {
+               modifier_setError((ModifierData *)smd, "Out of memory");
+               MEM_freeN(vert_edges);
+               MEM_freeN(adj_array);
+               return false;
+       }
+
+       smd->verts = MEM_mallocN(sizeof(*smd->verts) * numverts, "SDefBindVerts");
+       if (smd->verts == NULL) {
+               modifier_setError((ModifierData *)smd, "Out of memory");
+               freeAdjacencyMap(vert_edges, adj_array, edge_polys);
+               return false;
+       }
+
+       bvhtree_from_mesh_looptri(&treeData, tdm, 0.0, 2, 6);
+       if (treeData.tree == NULL) {
+               modifier_setError((ModifierData *)smd, "Out of memory");
+               freeAdjacencyMap(vert_edges, adj_array, edge_polys);
+               MEM_freeN(smd->verts);
+               smd->verts = NULL;
+               return false;
+       }
+
+       adj_result = buildAdjacencyMap(mpoly, medge, mloop, tnumpoly, tnumedges, vert_edges, adj_array, edge_polys);
+
+       if (adj_result == MOD_SDEF_BIND_RESULT_NONMANY_ERR) {
+               modifier_setError((ModifierData *)smd, "Target has edges with more than two polys");
+               freeAdjacencyMap(vert_edges, adj_array, edge_polys);
+               free_bvhtree_from_mesh(&treeData);
+               MEM_freeN(smd->verts);
+               smd->verts = NULL;
+               return false;
+       }
+
+       smd->numverts = numverts;
+       smd->numpoly = tnumpoly;
+
+       SDefBindCalcData data = {.treeData = &treeData,
+                                    .vert_edges = vert_edges,
+                                    .edge_polys = edge_polys,
+                                    .mpoly = mpoly,
+                                    .medge = medge,
+                                    .mloop = mloop,
+                                    .looptri = tdm->getLoopTriArray(tdm),
+                                    .mvert = tdm->getVertArray(tdm),
+                                    .bind_verts = smd->verts,
+                                    .vertexCos = vertexCos,
+                                    .falloff = smd->falloff,
+                                    .success = MOD_SDEF_BIND_RESULT_SUCCESS};
+
+       BLI_task_parallel_range_ex(0, numverts, &data, NULL, 0, bindVert,
+                                  numverts > 10000, false);
+
+       if (data.success == MOD_SDEF_BIND_RESULT_MEM_ERR) {
+               modifier_setError((ModifierData *)smd, "Out of memory");
+               freeData((ModifierData *)smd);
+       }
+       else if (data.success == MOD_SDEF_BIND_RESULT_NONMANY_ERR) {
+               modifier_setError((ModifierData *)smd, "Target has edges with more than two polys");
+               freeData((ModifierData *)smd);
+       }
+       else if (data.success == MOD_SDEF_BIND_RESULT_CONCAVE_ERR) {
+               modifier_setError((ModifierData *)smd, "Target contains concave polys");
+               freeData((ModifierData *)smd);
+       }
+       else if (data.success == MOD_SDEF_BIND_RESULT_OVERLAP_ERR) {
+               modifier_setError((ModifierData *)smd, "Target contains overlapping verts");
+               freeData((ModifierData *)smd);
+       }
+       else if (data.success == MOD_SDEF_BIND_RESULT_GENERIC_ERR) {
+               /* I know this message is vague, but I could not think of a way
+                * to explain this whith a reasonably sized message.
+                * Though it shouldn't really matter all that much,
+                * because this is very unlikely to occur */
+               modifier_setError((ModifierData *)smd, "Target contains invalid polys");
+               freeData((ModifierData *)smd);
+       }
+
+       freeAdjacencyMap(vert_edges, adj_array, edge_polys);
+       free_bvhtree_from_mesh(&treeData);
+
+       return data.success == 1;
+}
+
+static void deformVert(void *userdata, void *UNUSED(userdata_chunk), const int index, const int UNUSED(threadid))
+{
+       const SDefDeformData * const data = (SDefDeformData *)userdata;
+       const SDefBind *sdbind = data->bind_verts[index].binds;
+       const MVert * const mvert = data->mvert;
+       float * const vertexCos = data->vertexCos[index];
+       float norm[3], temp[3];
+
+       zero_v3(vertexCos);
+
+       for (int j = 0; j < data->bind_verts[index].numbinds; j++, sdbind++) {
+               /* Mode-generic operations (allocate poly coordinates) */
+               float (*coords)[3] = MEM_mallocN(sizeof(*coords) * sdbind->numverts, "SDefDoPolyCoords");
+
+               for (int k = 0; k < sdbind->numverts; k++) {
+                       copy_v3_v3(coords[k], mvert[sdbind->vert_inds[k]].co);
+               }
+
+               normal_poly_v3(norm, coords, sdbind->numverts);
+               zero_v3(temp);
+
+               /* ---------- looptri mode ---------- */
+               if (sdbind->mode == MOD_SDEF_MODE_LOOPTRI) {
+                       madd_v3_v3fl(temp, mvert[sdbind->vert_inds[0]].co, sdbind->vert_weights[0]);
+                       madd_v3_v3fl(temp, mvert[sdbind->vert_inds[1]].co, sdbind->vert_weights[1]);
+                       madd_v3_v3fl(temp, mvert[sdbind->vert_inds[2]].co, sdbind->vert_weights[2]);
+               }
+               else {
+                       /* ---------- ngon mode ---------- */
+                       if (sdbind->mode == MOD_SDEF_MODE_NGON) {
+                               for (int k = 0; k < sdbind->numverts; k++) {
+                                       madd_v3_v3fl(temp, coords[k], sdbind->vert_weights[k]);
+                               }
+                       }
+
+                       /* ---------- centroid mode ---------- */
+                       else if (sdbind->mode == MOD_SDEF_MODE_CENTROID) {
+                               float cent[3];
+                               mid_v3_v3_array(cent, coords, sdbind->numverts);
+
+                               madd_v3_v3fl(temp, mvert[sdbind->vert_inds[0]].co, sdbind->vert_weights[0]);
+                               madd_v3_v3fl(temp, mvert[sdbind->vert_inds[1]].co, sdbind->vert_weights[1]);
+                               madd_v3_v3fl(temp, cent, sdbind->vert_weights[2]);
+                       }
+               }
+
+               MEM_freeN(coords);
+
+               /* Apply normal offset (generic for all modes) */
+               madd_v3_v3fl(temp, norm, sdbind->normal_dist);
+
+               madd_v3_v3fl(vertexCos, temp, sdbind->influence);
+       }
+}
+
+static void surfacedeformModifier_do(ModifierData *md, float (*vertexCos)[3], unsigned int numverts)
+{
+       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+       DerivedMesh *tdm;
+       unsigned int tnumpoly;
+
+       /* Exit function if bind flag is not set (free bind data if any) */
+       if (!(smd->flags & MOD_SDEF_BIND)) {
+               freeData(md);
+               return;
+       }
+
+       /* Handle target mesh both in and out of edit mode */
+       if (smd->target == md->scene->obedit) {
+               BMEditMesh *em = BKE_editmesh_from_object(smd->target);
+               tdm = em->derivedFinal;
+       }
+       else {
+               tdm = smd->target->derivedFinal;
+       }
+
+       tnumpoly = tdm->getNumPolys(tdm);
+
+       /* If not bound, execute bind */
+       if (!(smd->verts)) {
+               if (!surfacedeformBind(smd, vertexCos, numverts, tnumpoly, tdm)) {
+                       smd->flags &= ~MOD_SDEF_BIND;
+                       return;
+               }
+       }
+
+       /* Poly count checks */
+       if (smd->numverts != numverts) {
+               modifier_setError(md, "Verts changed from %u to %u", smd->numverts, numverts);
+               tdm->release(tdm);
+               return;
+       }
+       else if (smd->numpoly != tnumpoly) {
+               modifier_setError(md, "Target polygons changed from %u to %u", smd->numpoly, tnumpoly);
+               tdm->release(tdm);
+               return;
+       }
+
+       /* Actual vertex location update starts here */
+       SDefDeformData data = {.bind_verts = smd->verts,
+                                  .mvert = tdm->getVertArray(tdm),
+                                  .vertexCos = vertexCos};
+
+       BLI_task_parallel_range_ex(0, numverts, &data, NULL, 0, deformVert,
+                                  numverts > 10000, false);
+
+       tdm->release(tdm);
+}
+
+static void deformVerts(ModifierData *md, Object *UNUSED(ob),
+                        DerivedMesh *UNUSED(derivedData),
+                        float (*vertexCos)[3], int numVerts,
+                        ModifierApplyFlag UNUSED(flag))
+{
+       surfacedeformModifier_do(md, vertexCos, numVerts);
+}
+
+static void deformVertsEM(ModifierData *md, Object *UNUSED(ob),
+                          struct BMEditMesh *UNUSED(editData),
+                          DerivedMesh *UNUSED(derivedData),
+                          float (*vertexCos)[3], int numVerts)
+{
+       surfacedeformModifier_do(md, vertexCos, numVerts);
+}
+
+static bool isDisabled(ModifierData *md, int UNUSED(useRenderParams))
+{
+       SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
+
+       return !smd->target;
+}
+
+ModifierTypeInfo modifierType_SurfaceDeform = {
+       /* name */              "Surface Deform",
+       /* structName */        "SurfaceDeformModifierData",
+       /* structSize */        sizeof(SurfaceDeformModifierData),
+       /* type */              eModifierTypeType_OnlyDeform,
+       /* flags */             eModifierTypeFlag_AcceptsMesh |
+                               eModifierTypeFlag_SupportsEditmode,
+
+       /* copyData */          copyData,
+       /* deformVerts */       deformVerts,
+       /* deformMatrices */    NULL,
+       /* deformVertsEM */     deformVertsEM,
+       /* deformMatricesEM */  NULL,
+       /* applyModifier */     NULL,
+       /* applyModifierEM */   NULL,
+       /* initData */          initData,
+       /* requiredDataMask */  NULL,
+       /* freeData */          freeData,
+       /* isDisabled */        isDisabled,
+       /* updateDepgraph */    updateDepgraph,
+       /* updateDepsgraph */   updateDepsgraph,
+       /* dependsOnTime */     NULL,
+       /* dependsOnNormals */  NULL,
+       /* foreachObjectLink */ foreachObjectLink,
+       /* foreachIDLink */     NULL,
+       /* foreachTexLink */    NULL,
+};
index 93414562ccfe438880f8ef47410d5c1e1d07c551..ded1f0b77e6f19636807a9369cdc43a9e7b9fda2 100644 (file)
@@ -287,5 +287,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
        INIT_TYPE(NormalEdit);
        INIT_TYPE(CorrectiveSmooth);
        INIT_TYPE(MeshSequenceCache);
+       INIT_TYPE(SurfaceDeform);
 #undef INIT_TYPE
 }