Corrective Smooth Modifier (aka delta-mush)
authorCampbell Barton <ideasman42@gmail.com>
Sat, 28 Mar 2015 17:44:05 +0000 (04:44 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 30 Mar 2015 23:20:11 +0000 (10:20 +1100)
This modifier can be used to correct bad deformations,

Original patch D1183 by @sazerac, with own modifications

18 files changed:
release/scripts/startup/bl_ui/properties_data_modifier.py
source/blender/blenkernel/BKE_editmesh.h
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/intern/editmesh.c
source/blender/blenkernel/intern/mesh.c
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_correctivesmooth.c [new file with mode: 0644]
source/blender/modifiers/intern/MOD_util.c

index f22976f5b6d0b5570cf7d0c3c78b0e6f348d5e83..a3cf079f915b1f15053ad87fca40d017fc5db5ad 100644 (file)
@@ -1386,6 +1386,31 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
         sub.active = has_vgroup
         sub.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT')
 
+    def CORRECTIVE_SMOOTH(self, layout, ob, md):
+        is_bind = md.is_bind
+
+        layout.prop(md, "iterations")
+        layout.prop(md, "lambda_factor", text="Factor")
+
+        row = layout.row()
+        row.prop(md, "smooth_type")
+
+        split = layout.split()
+
+        col = split.column()
+        col.label(text="Vertex Group:")
+        row = col.row(align=True)
+        row.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
+        row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT')
+
+        col = split.column()
+        col.prop(md, "use_only_smooth")
+        col.prop(md, "use_pin_boundary")
+
+        layout.prop(md, "rest_source")
+        if md.rest_source == 'BIND':
+            layout.operator("object.correctivesmooth_bind", text="Unbind" if is_bind else "Bind")
+
 
 if __name__ == "__main__":  # only for live edit.
     bpy.utils.register_module(__name__)
index d0b94117b2333705e47c3eb238a440cce5064635..d350eea7ac7252614dfa74ee8bfad308b8fe3cbf 100644 (file)
@@ -92,6 +92,7 @@ void        BKE_editmesh_update_linked_customdata(BMEditMesh *em);
 
 void        BKE_editmesh_color_free(BMEditMesh *em);
 void        BKE_editmesh_color_ensure(BMEditMesh *em, const char htype);
+float     (*BKE_editmesh_vertexCos_get_orco(BMEditMesh *em, int *r_numVerts))[3];
 
 /* editderivedmesh.c */
 /* should really be defined in editmesh.c, but they use 'EditDerivedBMesh' */
index 5cf557a07e5c954157b1855e2cc3dc71828904a5..05c20410458dcf3fbb56afbfeb0d7429306f812f 100644 (file)
@@ -119,7 +119,7 @@ bool BKE_mesh_uv_cdlayer_rename_index(struct Mesh *me, const int poly_index, con
                                       const char *new_name, const bool do_tessface);
 bool BKE_mesh_uv_cdlayer_rename(struct Mesh *me, const char *old_name, const char *new_name, bool do_tessface);
 
-float (*BKE_mesh_vertexCos_get(struct Mesh *me, int *r_numVerts))[3];
+float (*BKE_mesh_vertexCos_get(const struct Mesh *me, int *r_numVerts))[3];
 
 void BKE_mesh_calc_normals_split(struct Mesh *mesh);
 void BKE_mesh_split_faces(struct Mesh *mesh);
index 2247b91df1dd7eafa19ed7de439ee60fe8cd1ad5..87a5c6f149fb03010be534fb3db5a17bed69db9a 100644 (file)
@@ -246,3 +246,21 @@ void BKE_editmesh_color_ensure(BMEditMesh *em, const char htype)
                        break;
        }
 }
+
+float (*BKE_editmesh_vertexCos_get_orco(BMEditMesh *em, int *r_numVerts))[3]
+{
+       BMIter iter;
+       BMVert *eve;
+       float (*orco)[3];
+       int i;
+
+       orco = MEM_mallocN(em->bm->totvert * sizeof(*orco), __func__);
+
+       BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
+               copy_v3_v3(orco[i], eve->co);
+       }
+
+       *r_numVerts = em->bm->totvert;
+
+       return orco;
+}
index 8ebc318ff9eeef1ba106b4e950e04e53a6c43676..adce6e15732e7c59062215b7ef8a73e4d3240907 100644 (file)
@@ -1805,7 +1805,7 @@ void BKE_mesh_smooth_flag_set(Object *meshOb, int enableSmooth)
  * Return a newly MEM_malloc'd array of all the mesh vertex locations
  * \note \a r_numVerts may be NULL
  */
-float (*BKE_mesh_vertexCos_get(Mesh *me, int *r_numVerts))[3]
+float (*BKE_mesh_vertexCos_get(const Mesh *me, int *r_numVerts))[3]
 {
        int i, numVerts = me->totvert;
        float (*cos)[3] = MEM_mallocN(sizeof(*cos) * numVerts, "vertexcos1");
index b8c3bffae0fbaa6ccfc5d7d8be4066ac73119ce7..b0aa47787b6a01a892c77187107f21cbd4d8d1a3 100644 (file)
@@ -4949,6 +4949,20 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
                        }
                        lmd->cache_system = NULL;
                }
+               else if (md->type == eModifierType_CorrectiveSmooth) {
+                       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData*)md;
+
+                       if (csmd->bind_coords) {
+                               csmd->bind_coords = newdataadr(fd, csmd->bind_coords);
+                               if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
+                                       BLI_endian_switch_float_array((float *)csmd->bind_coords, csmd->bind_coords_num * 3);
+                               }
+                       }
+
+                       /* runtime only */
+                       csmd->delta_cache = NULL;
+                       csmd->delta_cache_num = 0;
+               }
        }
 }
 
index bc1bc9c7759ad12c778343bee6993b366cd52e10..afc53f343f1abfd284728b0b2fffd9348c7d46a5 100644 (file)
@@ -1620,6 +1620,13 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
 
                        writedata(wd, DATA, sizeof(float)*lmd->total_verts * 3, lmd->vertexco);
                }
+               else if (md->type == eModifierType_CorrectiveSmooth) {
+                       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+
+                       if (csmd->bind_coords) {
+                               writedata(wd, DATA, sizeof(float[3]) * csmd->bind_coords_num, csmd->bind_coords);
+                       }
+               }
        }
 }
 
index db040bc2017631fe174052d97b1ae679e9818532..d535457fc5b6b4f44901c7292c439a1ecec4c7c4 100644 (file)
@@ -174,6 +174,7 @@ void OBJECT_OT_multires_higher_levels_delete(struct wmOperatorType *ot);
 void OBJECT_OT_multires_base_apply(struct wmOperatorType *ot);
 void OBJECT_OT_multires_external_save(struct wmOperatorType *ot);
 void OBJECT_OT_multires_external_pack(struct wmOperatorType *ot);
+void OBJECT_OT_correctivesmooth_bind(struct wmOperatorType *ot);
 void OBJECT_OT_meshdeform_bind(struct wmOperatorType *ot);
 void OBJECT_OT_explode_refresh(struct wmOperatorType *ot);
 void OBJECT_OT_ocean_bake(struct wmOperatorType *ot);
index f9315214557538ba9baa2c025d71e302a9ef3131..ad02f78e7b6544cc460409d4d927a97f02db3347 100644 (file)
@@ -1815,6 +1815,73 @@ void OBJECT_OT_skin_armature_create(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
        edit_modifier_properties(ot);
 }
+/************************ delta mush bind operator *********************/
+
+static int correctivesmooth_poll(bContext *C)
+{
+       return edit_modifier_poll_generic(C, &RNA_CorrectiveSmoothModifier, 0);
+}
+
+static int correctivesmooth_bind_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene = CTX_data_scene(C);
+       Object *ob = ED_object_active_context(C);
+       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)edit_modifier_property_get(op, ob, eModifierType_CorrectiveSmooth);
+       bool is_bind;
+
+       if (!csmd) {
+               return OPERATOR_CANCELLED;
+       }
+
+       if (!modifier_isEnabled(scene, &csmd->modifier, eModifierMode_Realtime)) {
+               BKE_report(op->reports, RPT_ERROR, "Modifier is disabled");
+               return OPERATOR_CANCELLED;
+       }
+
+       is_bind = (csmd->bind_coords != NULL);
+
+       MEM_SAFE_FREE(csmd->bind_coords);
+       MEM_SAFE_FREE(csmd->delta_cache);
+
+       if (is_bind) {
+               /* toggle off */
+               csmd->bind_coords_num = 0;
+       }
+       else {
+               /* signal to modifier to recalculate */
+               csmd->bind_coords_num = (unsigned int)-1;
+       }
+
+       DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+
+       return OPERATOR_FINISHED;
+}
+
+static int correctivesmooth_bind_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+       if (edit_modifier_invoke_properties(C, op))
+               return correctivesmooth_bind_exec(C, op);
+       else
+               return OPERATOR_CANCELLED;
+}
+
+void OBJECT_OT_correctivesmooth_bind(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Corrective Smooth Bind";
+       ot->description = "Bind base pose in delta mush modifier";
+       ot->idname = "OBJECT_OT_correctivesmooth_bind";
+
+       /* api callbacks */
+       ot->poll = correctivesmooth_poll;
+       ot->invoke = correctivesmooth_bind_invoke;
+       ot->exec = correctivesmooth_bind_exec;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+       edit_modifier_properties(ot);
+}
 
 /************************ mdef bind operator *********************/
 
index 15eb9090ce553796936d2ebbe999777b12b4c754..422f0c12e519ea45f7433d3f6e8e63859a42d84b 100644 (file)
@@ -144,6 +144,7 @@ void ED_operatortypes_object(void)
        WM_operatortype_append(OBJECT_OT_skin_radii_equalize);
        WM_operatortype_append(OBJECT_OT_skin_armature_create);
 
+       WM_operatortype_append(OBJECT_OT_correctivesmooth_bind);
        WM_operatortype_append(OBJECT_OT_meshdeform_bind);
        WM_operatortype_append(OBJECT_OT_explode_refresh);
        WM_operatortype_append(OBJECT_OT_ocean_bake);
index 873b5f5319ff8b164db7c3a7600ba7f5e85a9e6b..420b73cee866119098a40d88e21ddc2de05ef93f 100644 (file)
@@ -1121,6 +1121,7 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
                                                UI_icon_draw(x, y, ICON_MOD_BEVEL); break;
                                        case eModifierType_Smooth:
                                        case eModifierType_LaplacianSmooth:
+                                       case eModifierType_CorrectiveSmooth:
                                                UI_icon_draw(x, y, ICON_MOD_SMOOTH); break;
                                        case eModifierType_SimpleDeform:
                                                UI_icon_draw(x, y, ICON_MOD_SIMPLEDEFORM); break;
index b32b228fda419e526bae9f5bb4b3e164972845f3..f3c61f0ab6c1764ca1aa183bec345ee9c62e191d 100644 (file)
@@ -84,6 +84,7 @@ typedef enum ModifierType {
        eModifierType_Wireframe         = 48,
        eModifierType_DataTransfer      = 49,
        eModifierType_NormalEdit        = 50,
+       eModifierType_CorrectiveSmooth  = 51,
        NUM_MODIFIER_TYPES
 } ModifierType;
 
@@ -1288,6 +1289,48 @@ enum {
        MOD_LAPLACIANSMOOTH_NORMALIZED      = (1 << 5),
 };
 
+
+typedef struct CorrectiveSmoothModifierData {
+       ModifierData modifier;
+
+       /* positions set during 'bind' operator
+        * use for MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND */
+       float (*bind_coords)[3];
+
+       /* note: -1 is used to bind */
+       unsigned int bind_coords_num;
+
+       float lambda;
+       short repeat, flag;
+       char smooth_type, rest_source;
+       char pad[2];
+
+       char defgrp_name[64];  /* MAX_VGROUP_NAME */
+
+       /* runtime-only cache (delta's between),
+        * delta's between the original positions and the smoothed positions */
+       float (*delta_cache)[3];
+       unsigned int delta_cache_num;
+       char pad2[4];
+} CorrectiveSmoothModifierData;
+
+enum {
+       MOD_CORRECTIVESMOOTH_SMOOTH_SIMPLE         = 0,
+       MOD_CORRECTIVESMOOTH_SMOOTH_LENGTH_WEIGHT    = 1,
+};
+
+enum {
+       MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO       = 0,
+       MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND       = 1,
+};
+
+/* Corrective Smooth modifier flags */
+enum {
+       MOD_CORRECTIVESMOOTH_INVERT_VGROUP         = (1 << 0),
+       MOD_CORRECTIVESMOOTH_ONLY_SMOOTH           = (1 << 1),
+       MOD_CORRECTIVESMOOTH_PIN_BOUNDARY          = (1 << 2),
+};
+
 typedef struct UVWarpModifierData {
        ModifierData modifier;
 
index 577f27f564e1a9e0c3ab544e4b41d74ce8a41de6..8aa1bc59b4345b64c86bab1e4fbbb323dcb46168 100644 (file)
@@ -202,6 +202,7 @@ extern StructRNA RNA_DampedTrackConstraint;
 extern StructRNA RNA_DataTransferModifier;
 extern StructRNA RNA_DecimateModifier;
 extern StructRNA RNA_DelaySensor;
+extern StructRNA RNA_CorrectiveSmoothModifier;
 extern StructRNA RNA_DisplaceModifier;
 extern StructRNA RNA_DisplaySafeAreas;
 extern StructRNA RNA_DistortedNoiseTexture;
index bde8fd73d854f70e0d459b0507aeb2631f348233..635336004f9b7ad399b592584d77e896faa8a044 100644 (file)
@@ -92,6 +92,7 @@ EnumPropertyItem modifier_type_items[] = {
        {0, "", 0, N_("Deform"), ""},
        {eModifierType_Armature, "ARMATURE", ICON_MOD_ARMATURE, "Armature", ""},
        {eModifierType_Cast, "CAST", ICON_MOD_CAST, "Cast", ""},
+    {eModifierType_CorrectiveSmooth, "CORRECTIVE_SMOOTH", ICON_MOD_SMOOTH, "Corrective Smooth", ""},
        {eModifierType_Curve, "CURVE", ICON_MOD_CURVE, "Curve", ""},
        {eModifierType_Displace, "DISPLACE", ICON_MOD_DISPLACE, "Displace", ""},
        {eModifierType_Hook, "HOOK", ICON_HOOK, "Hook", ""},
@@ -377,6 +378,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr)
                        return &RNA_DataTransferModifier;
                case eModifierType_NormalEdit:
                        return &RNA_NormalEditModifier;
+               case eModifierType_CorrectiveSmooth:
+                       return &RNA_CorrectiveSmoothModifier;
                /* Default */
                case eModifierType_None:
                case eModifierType_ShapeKey:
@@ -444,6 +447,7 @@ RNA_MOD_VGROUP_NAME_SET(Cast, defgrp_name);
 RNA_MOD_VGROUP_NAME_SET(Curve, name);
 RNA_MOD_VGROUP_NAME_SET(DataTransfer, defgrp_name);
 RNA_MOD_VGROUP_NAME_SET(Decimate, defgrp_name);
+RNA_MOD_VGROUP_NAME_SET(CorrectiveSmooth, defgrp_name);
 RNA_MOD_VGROUP_NAME_SET(Displace, defgrp_name);
 RNA_MOD_VGROUP_NAME_SET(Hook, name);
 RNA_MOD_VGROUP_NAME_SET(LaplacianDeform, anchor_grp_name);
@@ -1034,6 +1038,33 @@ static EnumPropertyItem *rna_DataTransferModifier_mix_mode_itemf(bContext *C, Po
        return item;
 }
 
+static void rna_CorrectiveSmoothModifier_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)ptr->data;
+
+       MEM_SAFE_FREE(csmd->delta_cache);
+
+       rna_Modifier_update(bmain, scene, ptr);
+}
+
+static void rna_CorrectiveSmoothModifier_rest_source_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)ptr->data;
+
+       if (csmd->rest_source != MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) {
+               MEM_SAFE_FREE(csmd->bind_coords);
+               csmd->bind_coords_num = 0;
+       }
+
+       rna_CorrectiveSmoothModifier_update(bmain, scene, ptr);
+}
+
+static int rna_CorrectiveSmoothModifier_is_bind_get(PointerRNA *ptr)
+{
+       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)ptr->data;
+       return (csmd->bind_coords != NULL);
+}
+
 #else
 
 static PropertyRNA *rna_def_property_subdivision_common(StructRNA *srna, const char type[])
@@ -2111,6 +2142,90 @@ static void rna_def_modifier_smooth(BlenderRNA *brna)
        RNA_def_property_update(prop, 0, "rna_Modifier_update");
 }
 
+
+static void rna_def_modifier_correctivesmooth(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       static EnumPropertyItem modifier_smooth_type_items[] = {
+               {MOD_CORRECTIVESMOOTH_SMOOTH_SIMPLE, "SIMPLE", 0, "Simple",
+                "Use the average of adjacent edge-vertices"},
+               {MOD_CORRECTIVESMOOTH_SMOOTH_LENGTH_WEIGHT, "LENGTH_WEIGHTED", 0, "Length Weight",
+                "Use the average of adjacent edge-vertices weighted by their length"},
+               {0, NULL, 0, NULL, NULL}
+       };
+
+       static EnumPropertyItem modifier_rest_source_items[] = {
+               {MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO, "ORCO", 0, "Original Coords",
+                "Use base mesh vert coords as the rest position"},
+               {MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND, "BIND", 0, "Bind Coords",
+                "Use bind vert coords for rest position"},
+               {0, NULL, 0, NULL, NULL}
+       };
+
+       srna = RNA_def_struct(brna, "CorrectiveSmoothModifier", "Modifier");
+       RNA_def_struct_ui_text(srna, "Corrective Smooth Modifier", "Correct distortion caused by deformation");
+       RNA_def_struct_sdna(srna, "CorrectiveSmoothModifierData");
+       RNA_def_struct_ui_icon(srna, ICON_MOD_SMOOTH);
+
+       prop = RNA_def_property(srna, "lambda_factor", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "lambda");
+       RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
+       RNA_def_property_ui_range(prop, 0.0, 1.0, 5, 3);
+       RNA_def_property_ui_text(prop, "Lambda Factor", "Smooth factor effect");
+       RNA_def_property_update(prop, 0, "rna_CorrectiveSmoothModifier_update");
+
+       prop = RNA_def_property(srna, "iterations", PROP_INT, PROP_NONE);
+       RNA_def_property_int_sdna(prop, NULL, "repeat");
+       RNA_def_property_ui_range(prop, 0, 200, 1, -1);
+       RNA_def_property_ui_text(prop, "Repeat", "");
+       RNA_def_property_update(prop, 0, "rna_CorrectiveSmoothModifier_update");
+
+       prop = RNA_def_property(srna, "rest_source", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "rest_source");
+       RNA_def_property_enum_items(prop, modifier_rest_source_items);
+       RNA_def_property_ui_text(prop, "Rest Source", "Select the source of rest positions");
+       RNA_def_property_update(prop, 0, "rna_CorrectiveSmoothModifier_rest_source_update");
+
+       prop = RNA_def_property(srna, "smooth_type", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "smooth_type");
+       RNA_def_property_enum_items(prop, modifier_smooth_type_items);
+       RNA_def_property_ui_text(prop, "Smooth Type", "Method used for smoothing");
+       RNA_def_property_update(prop, 0, "rna_CorrectiveSmoothModifier_update");
+
+       prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_CORRECTIVESMOOTH_INVERT_VGROUP);
+       RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence");
+       RNA_def_property_update(prop, 0, "rna_CorrectiveSmoothModifier_update");
+
+       prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE);
+       RNA_def_property_string_sdna(prop, NULL, "defgrp_name");
+       RNA_def_property_ui_text(prop, "Vertex Group",
+                                "Name of Vertex Group which determines influence of modifier per point");
+       RNA_def_property_string_funcs(prop, NULL, NULL, "rna_CorrectiveSmoothModifier_defgrp_name_set");
+       RNA_def_property_update(prop, 0, "rna_CorrectiveSmoothModifier_update");
+
+       prop = RNA_def_property(srna, "is_bind", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_ui_text(prop, "Bind current shape", "");
+       RNA_def_property_boolean_funcs(prop, "rna_CorrectiveSmoothModifier_is_bind_get", NULL);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+       RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+       prop = RNA_def_property(srna, "use_only_smooth", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_CORRECTIVESMOOTH_ONLY_SMOOTH);
+       RNA_def_property_ui_text(prop, "Only Smooth",
+                                "Apply smoothing without reconstructing the surface");
+       RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+       prop = RNA_def_property(srna, "use_pin_boundary", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_CORRECTIVESMOOTH_PIN_BOUNDARY);
+       RNA_def_property_ui_text(prop, "Pin Boundaries",
+                                "Excludes boundary vertices from being smoothed");
+       RNA_def_property_update(prop, 0, "rna_CorrectiveSmoothModifier_update");
+}
+
+
 static void rna_def_modifier_laplaciansmooth(BlenderRNA *brna)
 {
        StructRNA *srna;
@@ -4520,6 +4635,7 @@ void RNA_def_modifier(BlenderRNA *brna)
        rna_def_modifier_displace(brna);
        rna_def_modifier_uvproject(brna);
        rna_def_modifier_smooth(brna);
+       rna_def_modifier_correctivesmooth(brna);
        rna_def_modifier_cast(brna);
        rna_def_modifier_meshdeform(brna);
        rna_def_modifier_particlesystem(brna);
index 57e927fffed0d886d24c81b604ec0484b78ed194..1de3fc9eac02e7a7fe62575fe717d86ac9a96164 100644 (file)
@@ -51,6 +51,7 @@ set(SRC
        intern/MOD_cast.c
        intern/MOD_cloth.c
        intern/MOD_collision.c
+       intern/MOD_correctivesmooth.c
        intern/MOD_curve.c
        intern/MOD_datatransfer.c
        intern/MOD_decimate.c
index 66e613be68a89b501377fd8ce35ffca4c1691ba6..a5d967599521b22fa52c3cbec3796fd70312ee39 100644 (file)
@@ -83,6 +83,7 @@ extern ModifierTypeInfo modifierType_LaplacianDeform;
 extern ModifierTypeInfo modifierType_Wireframe;
 extern ModifierTypeInfo modifierType_DataTransfer;
 extern ModifierTypeInfo modifierType_NormalEdit;
+extern ModifierTypeInfo modifierType_CorrectiveSmooth;
 
 /* MOD_util.c */
 void modifier_type_init(ModifierTypeInfo *types[]);
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
new file mode 100644 (file)
index 0000000..82c1d6f
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+* ***** 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) 2015 by the Blender Foundation.
+* All rights reserved.
+*
+* Contributor(s): Jack Simpson,
+*                 Campbell Barton
+*
+* ***** END GPL LICENSE BLOCK *****
+*
+*/
+
+/** \file blender/modifiers/intern/MOD_correctivesmooth.c
+ *  \ingroup modifiers
+ *
+ * Method of smoothing deformation, also known as 'delta-mush'.
+ */
+
+#include "DNA_scene_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_deform.h"
+#include "BKE_mesh.h"
+#include "BKE_editmesh.h"
+
+#include "MOD_modifiertypes.h"
+#include "MOD_util.h"
+
+#include "BLI_strict_flags.h"
+
+
+// #define DEBUG_TIME
+
+#include "PIL_time.h"
+#ifdef DEBUG_TIME
+#  include "PIL_time_utildefines.h"
+#endif
+
+/* minor optimization, calculate this inline */
+#define USE_TANGENT_CALC_INLINE
+
+static void initData(ModifierData *md)
+{
+       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+
+       csmd->bind_coords = NULL;
+       csmd->bind_coords_num = 0;
+
+       csmd->lambda = 0.5f;
+       csmd->repeat = 5;
+       csmd->flag = 0;
+       csmd->smooth_type = MOD_CORRECTIVESMOOTH_SMOOTH_SIMPLE;
+
+       csmd->defgrp_name[0] = '\0';
+
+       csmd->delta_cache = NULL;
+}
+
+
+static void copyData(ModifierData *md, ModifierData *target)
+{
+       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+       CorrectiveSmoothModifierData *tcsmd = (CorrectiveSmoothModifierData *)target;
+
+       modifier_copyData_generic(md, target);
+
+       if (csmd->bind_coords) {
+               tcsmd->bind_coords = MEM_dupallocN(csmd->bind_coords);
+       }
+
+       tcsmd->delta_cache = NULL;
+       tcsmd->delta_cache_num = 0;
+}
+
+
+static void freeBind(CorrectiveSmoothModifierData *csmd)
+{
+       MEM_SAFE_FREE(csmd->bind_coords);
+       MEM_SAFE_FREE(csmd->delta_cache);
+
+       csmd->bind_coords_num = 0;
+}
+
+
+static void freeData(ModifierData *md)
+{
+       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+       freeBind(csmd);
+}
+
+
+static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
+{
+       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+       CustomDataMask dataMask = 0;
+       /* ask for vertex groups if we need them */
+       if (csmd->defgrp_name[0]) {
+               dataMask |= CD_MASK_MDEFORMVERT;
+       }
+       return dataMask;
+}
+
+
+/* check individual weights for changes and cache values */
+static void dm_get_weights(
+        MDeformVert *dvert, const int defgrp_index,
+        const unsigned int numVerts, const bool use_invert_vgroup,
+        float *smooth_weights)
+{
+       unsigned int i;
+
+       for (i = 0; i < numVerts; i++, dvert++) {
+               const float w = defvert_find_weight(dvert, defgrp_index);
+
+               if (use_invert_vgroup == false) {
+                       smooth_weights[i] = w;
+               }
+               else {
+                       smooth_weights[i] = 1.0f - w;
+               }
+       }
+}
+
+
+static void dm_get_boundaries(DerivedMesh *dm, float *smooth_weights)
+{
+       const MPoly *mpoly = dm->getPolyArray(dm);
+       const MLoop *mloop = dm->getLoopArray(dm);
+       const MEdge *medge = dm->getEdgeArray(dm);
+       unsigned int mpoly_num, medge_num, i;
+       unsigned short *boundaries;
+
+       mpoly_num = (unsigned int)dm->getNumPolys(dm);
+       medge_num = (unsigned int)dm->getNumEdges(dm);
+
+       boundaries = MEM_callocN(medge_num * sizeof(*boundaries), __func__);
+
+       /* count the number of adjacent faces */
+       for (i = 0; i < mpoly_num; i++) {
+               const MPoly *p = &mpoly[i];
+               const int totloop = p->totloop;
+               int j;
+               for (j = 0; j < totloop; j++) {
+                       boundaries[mloop[p->loopstart + j].e]++;
+               }
+       }
+
+       for (i = 0; i < medge_num; i++) {
+               if (boundaries[i] == 1) {
+                       smooth_weights[medge[i].v1] = 0.0f;
+                       smooth_weights[medge[i].v2] = 0.0f;
+               }
+       }
+
+       MEM_freeN(boundaries);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Simple Weighted Smoothing
+ *
+ * (average of surrounding verts)
+ */
+static void smooth_iter__simple(
+        CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+        float (*vertexCos)[3], unsigned int numVerts,
+        const float *smooth_weights,
+        unsigned int iterations)
+{
+       const float lambda = csmd->lambda;
+       unsigned int i;
+
+       const unsigned int numEdges = (unsigned int)dm->getNumEdges(dm);
+       const MEdge *edges = dm->getEdgeArray(dm);
+       float *vertex_edge_count_div;
+
+       struct SmoothingData_Simple {
+               float delta[3];
+       } *smooth_data = MEM_callocN((size_t)numVerts * sizeof(*smooth_data), __func__);
+
+       vertex_edge_count_div = MEM_callocN((size_t)numVerts * sizeof(float), __func__);
+
+       /* calculate as floats to avoid int->float conversion in #smooth_iter */
+       for (i = 0; i < numEdges; i++) {
+               vertex_edge_count_div[edges[i].v1] += 1.0f;
+               vertex_edge_count_div[edges[i].v2] += 1.0f;
+       }
+
+       /* a little confusing, but we can include 'lambda' and smoothing weight
+        * here to avoid multiplying for every iteration */
+       if (smooth_weights == NULL) {
+               for (i = 0; i < numVerts; i++) {
+                       vertex_edge_count_div[i] =
+                               lambda * (vertex_edge_count_div[i] ? (1.0f / vertex_edge_count_div[i]) : 1.0f);
+               }
+       }
+       else {
+               for (i = 0; i < numVerts; i++) {
+                       vertex_edge_count_div[i] =
+                               smooth_weights[i] * lambda * (vertex_edge_count_div[i] ? (1.0f / vertex_edge_count_div[i]) : 1.0f);
+               }
+       }
+
+       /* -------------------------------------------------------------------- */
+       /* Main Smoothing Loop */
+
+       while (iterations--) {
+               for (i = 0; i < numEdges; i++) {
+                       struct SmoothingData_Simple *sd_v1;
+                       struct SmoothingData_Simple *sd_v2;
+                       float edge_dir[3];
+
+                       sub_v3_v3v3(edge_dir, vertexCos[edges[i].v2], vertexCos[edges[i].v1]);
+
+                       sd_v1 = &smooth_data[edges[i].v1];
+                       sd_v2 = &smooth_data[edges[i].v2];
+
+                       add_v3_v3(sd_v1->delta, edge_dir);
+                       sub_v3_v3(sd_v2->delta, edge_dir);
+               }
+
+
+               for (i = 0; i < numVerts; i++) {
+                       struct SmoothingData_Simple *sd = &smooth_data[i];
+                       madd_v3_v3fl(vertexCos[i], sd->delta, vertex_edge_count_div[i]);
+                       /* zero for the next iteration (saves memset on entire array) */
+                       memset(sd, 0, sizeof(*sd));
+               }
+       }
+
+       MEM_freeN(vertex_edge_count_div);
+       MEM_freeN(smooth_data);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Edge-Length Weighted Smoothing
+ */
+static void smooth_iter__length_weight(
+        CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+        float (*vertexCos)[3], unsigned int numVerts,
+        const float *smooth_weights,
+        unsigned int iterations)
+{
+       const float eps = FLT_EPSILON * 10.0f;
+       const unsigned int numEdges = (unsigned int)dm->getNumEdges(dm);
+       /* note: the way this smoothing method works, its approx half as strong as the simple-smooth,
+        * and 2.0 rarely spikes, double the value for consistent behavior. */
+       const float lambda = csmd->lambda * 2.0f;
+       const MEdge *edges = dm->getEdgeArray(dm);
+       float *vertex_edge_count;
+       unsigned int i;
+
+       struct SmoothingData_Weighted {
+               float delta[3];
+               float edge_length_sum;
+       } *smooth_data = MEM_callocN((size_t)numVerts * sizeof(*smooth_data), __func__);
+
+
+       /* calculate as floats to avoid int->float conversion in #smooth_iter */
+       vertex_edge_count = MEM_callocN((size_t)numVerts * sizeof(float), __func__);
+       for (i = 0; i < numEdges; i++) {
+               vertex_edge_count[edges[i].v1] += 1.0f;
+               vertex_edge_count[edges[i].v2] += 1.0f;
+       }
+
+
+       /* -------------------------------------------------------------------- */
+       /* Main Smoothing Loop */
+
+       while (iterations--) {
+               for (i = 0; i < numEdges; i++) {
+                       struct SmoothingData_Weighted *sd_v1;
+                       struct SmoothingData_Weighted *sd_v2;
+                       float edge_dir[3];
+                       float edge_dist;
+
+                       sub_v3_v3v3(edge_dir, vertexCos[edges[i].v2], vertexCos[edges[i].v1]);
+                       edge_dist = len_v3(edge_dir);
+
+                       /* weight by distance */
+                       mul_v3_fl(edge_dir, edge_dist);
+
+
+                       sd_v1 = &smooth_data[edges[i].v1];
+                       sd_v2 = &smooth_data[edges[i].v2];
+
+                       add_v3_v3(sd_v1->delta, edge_dir);
+                       sub_v3_v3(sd_v2->delta, edge_dir);
+
+                       sd_v1->edge_length_sum += edge_dist;
+                       sd_v2->edge_length_sum += edge_dist;
+               }
+
+               if (smooth_weights == NULL) {
+                       /* fast-path */
+                       for (i = 0; i < numVerts; i++) {
+                               struct SmoothingData_Weighted *sd = &smooth_data[i];
+                               /* divide by sum of all neighbour distances (weighted) and amount of neighbours, (mean average) */
+                               const float div = sd->edge_length_sum * vertex_edge_count[i];
+                               if (div > eps) {
+#if 0
+                                       /* first calculate the new location */
+                                       mul_v3_fl(sd->delta, 1.0f / div);
+                                       /* then interpolate */
+                                       madd_v3_v3fl(vertexCos[i], sd->delta, lambda);
+#else
+                                       /* do this in one step */
+                                       madd_v3_v3fl(vertexCos[i], sd->delta, lambda / div);
+#endif
+                               }
+                               /* zero for the next iteration (saves memset on entire array) */
+                               memset(sd, 0, sizeof(*sd));
+                       }
+               }
+               else {
+                       for (i = 0; i < numVerts; i++) {
+                               struct SmoothingData_Weighted *sd = &smooth_data[i];
+                               const float div = sd->edge_length_sum * vertex_edge_count[i];
+                               if (div > eps) {
+                                       const float lambda_w = lambda * smooth_weights[i];
+                                       madd_v3_v3fl(vertexCos[i], sd->delta, lambda_w / div);
+                               }
+
+                               memset(sd, 0, sizeof(*sd));
+                       }
+               }
+       }
+
+       MEM_freeN(vertex_edge_count);
+       MEM_freeN(smooth_data);
+}
+
+
+static void smooth_iter(
+        CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+        float (*vertexCos)[3], unsigned int numVerts,
+        const float *smooth_weights,
+        unsigned int iterations)
+{
+       switch (csmd->smooth_type) {
+               case MOD_CORRECTIVESMOOTH_SMOOTH_LENGTH_WEIGHT:
+                       smooth_iter__length_weight(csmd, dm, vertexCos, numVerts, smooth_weights, iterations);
+                       break;
+
+               /* case MOD_CORRECTIVESMOOTH_SMOOTH_SIMPLE: */
+               default:
+                       smooth_iter__simple(csmd, dm, vertexCos, numVerts, smooth_weights, iterations);
+                       break;
+       }
+}
+
+static void smooth_verts(
+        CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+        MDeformVert *dvert, const int defgrp_index,
+        float (*vertexCos)[3], unsigned int numVerts)
+{
+       float *smooth_weights = NULL;
+
+       if (dvert || (csmd->flag & MOD_CORRECTIVESMOOTH_PIN_BOUNDARY)) {
+
+               smooth_weights = MEM_mallocN(numVerts * sizeof(float), __func__);
+
+               if (dvert) {
+                       dm_get_weights(
+                               dvert, defgrp_index,
+                               numVerts, (csmd->flag & MOD_CORRECTIVESMOOTH_INVERT_VGROUP) != 0,
+                               smooth_weights);
+               }
+               else {
+                       fill_vn_fl(smooth_weights, (int)numVerts, 1.0f);
+               }
+
+               if (csmd->flag & MOD_CORRECTIVESMOOTH_PIN_BOUNDARY) {
+                       dm_get_boundaries(dm, smooth_weights);
+               }
+       }
+
+       smooth_iter(csmd, dm, vertexCos, numVerts, smooth_weights, (unsigned int)csmd->repeat);
+
+       if (smooth_weights) {
+               MEM_freeN(smooth_weights);
+       }
+}
+
+/**
+ * finalize after accumulation.
+ */
+static void calc_tangent_ortho(float ts[3][3])
+{
+       float v_tan_a[3], v_tan_b[3];
+       float t_vec_a[3], t_vec_b[3];
+
+       normalize_v3(ts[2]);
+
+       copy_v3_v3(v_tan_a, ts[0]);
+       copy_v3_v3(v_tan_b, ts[1]);
+
+       cross_v3_v3v3(ts[1], ts[2], v_tan_a);
+       mul_v3_fl(ts[1], dot_v3v3(ts[1], v_tan_b) < 0.0f ? -1.0f : 1.0f);
+
+       /* orthognalise tangent */
+       mul_v3_v3fl(t_vec_a, ts[2], dot_v3v3(ts[2], v_tan_a));
+       sub_v3_v3v3(ts[0], v_tan_a, t_vec_a);
+
+       /* orthognalise bitangent */
+       mul_v3_v3fl(t_vec_a, ts[2], dot_v3v3(ts[2], ts[1]));
+       mul_v3_v3fl(t_vec_b, ts[0], dot_v3v3(ts[0], ts[1]) / dot_v3v3(v_tan_a, v_tan_a));
+       sub_v3_v3(ts[1], t_vec_a);
+       sub_v3_v3(ts[1], t_vec_b);
+
+       normalize_v3(ts[0]);
+       normalize_v3(ts[1]);
+}
+
+/**
+ * accumulate edge-vectors from all polys.
+ */
+static void calc_tangent_loop_accum(
+        const float v_dir_prev[3],
+        const float v_dir_next[3],
+        float r_tspace[3][3])
+{
+       add_v3_v3v3(r_tspace[1], v_dir_prev, v_dir_next);
+
+       if (compare_v3v3(v_dir_prev, v_dir_next, FLT_EPSILON * 10.0f) == false) {
+               const float weight = fabsf(acosf(dot_v3v3(v_dir_next, v_dir_prev)));
+               float nor[3];
+
+               cross_v3_v3v3(nor, v_dir_prev, v_dir_next);
+               normalize_v3(nor);
+
+               cross_v3_v3v3(r_tspace[0], r_tspace[1], nor);
+
+               mul_v3_fl(nor, weight);
+               /* accumulate weighted normals */
+               add_v3_v3(r_tspace[2], nor);
+       }
+}
+
+
+static void calc_tangent_spaces(
+        DerivedMesh *dm, float (*vertexCos)[3],
+        float (*r_tangent_spaces)[3][3])
+{
+       const unsigned int mpoly_num = (unsigned int)dm->getNumPolys(dm);
+#ifndef USE_TANGENT_CALC_INLINE
+       const unsigned int mvert_num = (unsigned int)dm->getNumVerts(dm);
+#endif
+       const MPoly *mpoly = dm->getPolyArray(dm);
+       const MLoop *mloop = dm->getLoopArray(dm);
+       unsigned int i;
+
+       for (i = 0; i < mpoly_num; i++) {
+               const MPoly *mp = &mpoly[i];
+               const MLoop *l_next = &mloop[mp->loopstart];
+               const MLoop *l_term = l_next + mp->totloop;
+               const MLoop *l_prev = l_term - 2;
+               const MLoop *l_curr = l_term - 1;
+
+               /* loop directions */
+               float v_dir_prev[3], v_dir_next[3];
+
+               /* needed entering the loop */
+               sub_v3_v3v3(v_dir_prev, vertexCos[l_prev->v], vertexCos[l_curr->v]);
+               normalize_v3(v_dir_prev);
+
+               for (;
+                    l_next != l_term;
+                    l_prev = l_curr, l_curr = l_next, l_next++)
+               {
+                       float (*ts)[3] = r_tangent_spaces[l_curr->v];
+
+                       /* re-use the previous value */
+#if 0
+                       sub_v3_v3v3(v_dir_prev, vertexCos[l_prev->v], vertexCos[l_curr->v]);
+                       normalize_v3(v_dir_prev);
+#endif
+                       sub_v3_v3v3(v_dir_next, vertexCos[l_curr->v], vertexCos[l_next->v]);
+                       normalize_v3(v_dir_next);
+
+                       calc_tangent_loop_accum(v_dir_prev, v_dir_next, ts);
+
+                       copy_v3_v3(v_dir_prev, v_dir_next);
+               }
+       }
+
+       /* do inline */
+#ifndef USE_TANGENT_CALC_INLINE
+       for (i = 0; i < mvert_num; i++) {
+               float (*ts)[3] = r_tangent_spaces[i];
+               calc_tangent_ortho(ts);
+       }
+#endif
+}
+
+/**
+ * This calculates #CorrectiveSmoothModifierData.delta_cache
+ * It's not run on every update (during animation for example).
+ */
+static void calc_deltas(
+        CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+        MDeformVert *dvert, const int defgrp_index,
+        const float (*rest_coords)[3], unsigned int numVerts)
+{
+       float (*smooth_vertex_coords)[3] = MEM_dupallocN(rest_coords);
+       float (*tangent_spaces)[3][3];
+       unsigned int i;
+
+       tangent_spaces = MEM_callocN((size_t)(numVerts) * sizeof(float[3][3]), __func__);
+
+       if (csmd->delta_cache_num != numVerts) {
+               MEM_SAFE_FREE(csmd->delta_cache);
+       }
+
+       /* allocate deltas if they have not yet been allocated, otheriwse we will just write over them */
+       if (!csmd->delta_cache) {
+               csmd->delta_cache_num = numVerts;
+               csmd->delta_cache = MEM_mallocN((size_t)numVerts * sizeof(float[3]), __func__);
+       }
+
+       smooth_verts(csmd, dm, dvert, defgrp_index, smooth_vertex_coords, numVerts);
+
+       calc_tangent_spaces(dm, smooth_vertex_coords, tangent_spaces);
+
+       for (i = 0; i < numVerts; i++) {
+               float imat[3][3], delta[3];
+
+#ifdef USE_TANGENT_CALC_INLINE
+               calc_tangent_ortho(tangent_spaces[i]);
+#endif
+
+               sub_v3_v3v3(delta, rest_coords[i], smooth_vertex_coords[i]);
+               if (UNLIKELY(!invert_m3_m3(imat, tangent_spaces[i]))) {
+                       transpose_m3_m3(imat, tangent_spaces[i]);
+               }
+               mul_v3_m3v3(csmd->delta_cache[i], imat, delta);
+       }
+
+       MEM_freeN(tangent_spaces);
+       MEM_freeN(smooth_vertex_coords);
+}
+
+
+static void correctivesmooth_modifier_do(
+        ModifierData *md, Object *ob, DerivedMesh *dm,
+        float (*vertexCos)[3], unsigned int numVerts,
+        struct BMEditMesh *em)
+{
+       CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+
+       const bool force_delta_cache_update =
+               /* XXX, take care! if mesh data its self changes we need to forcefully recalculate deltas */
+               ((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO) &&
+                (((ID *)ob->data)->flag & LIB_ID_RECALC));
+
+       bool use_only_smooth = (csmd->flag & MOD_CORRECTIVESMOOTH_ONLY_SMOOTH) != 0;
+       MDeformVert *dvert = NULL;
+       int defgrp_index;
+
+       modifier_get_vgroup(ob, dm, csmd->defgrp_name, &dvert, &defgrp_index);
+
+       /* if rest bind_coords not are defined, set them (only run during bind) */
+       if ((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) &&
+           /* signal to recalculate, whoever sets MUST also free bind coords */
+           (csmd->bind_coords_num == (unsigned int)-1))
+       {
+               BLI_assert(csmd->bind_coords == NULL);
+               csmd->bind_coords = MEM_dupallocN(vertexCos);
+               csmd->bind_coords_num = numVerts;
+               BLI_assert(csmd->bind_coords != NULL);
+       }
+
+       if (UNLIKELY(use_only_smooth)) {
+               smooth_verts(csmd, dm, dvert, defgrp_index, vertexCos, numVerts);
+               return;
+       }
+
+       if ((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) && (csmd->bind_coords == NULL)) {
+               modifier_setError(md, "Bind data required");
+               goto error;
+       }
+
+       /* If the number of verts has changed, the bind is invalid, so we do nothing */
+       if (csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) {
+               if (csmd->bind_coords_num != numVerts) {
+                       modifier_setError(md, "Bind vertex count mismatch: %d to %d", csmd->bind_coords_num, numVerts);
+                       goto error;
+               }
+       }
+       else {
+               /* MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO */
+               if (ob->type != OB_MESH) {
+                       modifier_setError(md, "Object is not a mesh");
+                       goto error;
+               }
+               else {
+                       int me_numVerts = (em) ? em->bm->totvert : ((Mesh *)ob->data)->totvert;
+
+                       if ((unsigned int)me_numVerts != numVerts) {
+                               modifier_setError(md, "Original vertex count mismatch: %d to %d", me_numVerts, numVerts);
+                               goto error;
+                       }
+               }
+       }
+
+       /* check to see if our deltas are still valid */
+       if (!csmd->delta_cache || (csmd->delta_cache_num != numVerts) || force_delta_cache_update) {
+               const float (*rest_coords)[3];
+               bool is_rest_coords_alloc = false;
+
+               if (csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) {
+                       /* caller needs to do sanity check here */
+                       csmd->bind_coords_num = numVerts;
+                       rest_coords = (const float (*)[3])csmd->bind_coords;
+               }
+               else {
+                       int me_numVerts;
+                       rest_coords = (const float (*)[3]) ((em) ?
+                               BKE_editmesh_vertexCos_get_orco(em, &me_numVerts) :
+                               BKE_mesh_vertexCos_get(ob->data, &me_numVerts));
+
+                       BLI_assert((unsigned int)me_numVerts == numVerts);
+                       is_rest_coords_alloc = true;
+               }
+
+#ifdef DEBUG_TIME
+       TIMEIT_START(corrective_smooth_deltas);
+#endif
+
+               calc_deltas(csmd, dm, dvert, defgrp_index, rest_coords, numVerts);
+
+#ifdef DEBUG_TIME
+       TIMEIT_END(corrective_smooth_deltas);
+#endif
+               if (is_rest_coords_alloc) {
+                       MEM_freeN((void *)rest_coords);
+               }
+       }
+
+       if (csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) {
+               /* this could be a check, but at this point it _must_ be valid */
+               BLI_assert(csmd->bind_coords_num == numVerts && csmd->delta_cache);
+       }
+
+
+#ifdef DEBUG_TIME
+       TIMEIT_START(corrective_smooth);
+#endif
+
+       /* do the actual delta mush */
+       smooth_verts(csmd, dm, dvert, defgrp_index, vertexCos, numVerts);
+
+       {
+               unsigned int i;
+
+               float (*tangent_spaces)[3][3];
+
+               /* calloc, since values are accumulated */
+               tangent_spaces = MEM_callocN((size_t)numVerts * sizeof(float[3][3]), __func__);
+
+               calc_tangent_spaces(dm, vertexCos, tangent_spaces);
+
+               for (i = 0; i < numVerts; i++) {
+                       float delta[3];
+
+#ifdef USE_TANGENT_CALC_INLINE
+                       calc_tangent_ortho(tangent_spaces[i]);
+#endif
+
+                       mul_v3_m3v3(delta, tangent_spaces[i], csmd->delta_cache[i]);
+                       add_v3_v3(vertexCos[i], delta);
+               }
+
+               MEM_freeN(tangent_spaces);
+       }
+
+#ifdef DEBUG_TIME
+       TIMEIT_END(corrective_smooth);
+#endif
+
+       return;
+
+       /* when the modifier fails to execute */
+error:
+       MEM_SAFE_FREE(csmd->delta_cache);
+       csmd->delta_cache_num = 0;
+
+}
+
+
+static void deformVerts(
+        ModifierData *md, Object *ob, DerivedMesh *derivedData,
+        float (*vertexCos)[3], int numVerts, ModifierApplyFlag UNUSED(flag))
+{
+       DerivedMesh *dm = get_dm(ob, NULL, derivedData, NULL, false, false);
+
+       correctivesmooth_modifier_do(md, ob, dm, vertexCos, (unsigned int)numVerts, NULL);
+
+       if (dm != derivedData) {
+               dm->release(dm);
+       }
+}
+
+
+static void deformVertsEM(
+        ModifierData *md, Object *ob, struct BMEditMesh *editData,
+        DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts)
+{
+       DerivedMesh *dm = get_dm(ob, editData, derivedData, NULL, false, false);
+
+       correctivesmooth_modifier_do(md, ob, dm, vertexCos, (unsigned int)numVerts, editData);
+
+       if (dm != derivedData) {
+               dm->release(dm);
+       }
+}
+
+
+ModifierTypeInfo modifierType_CorrectiveSmooth = {
+       /* name */              "CorrectiveSmooth",
+       /* structName */        "CorrectiveSmoothModifierData",
+       /* structSize */        sizeof(CorrectiveSmoothModifierData),
+       /* 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 */  requiredDataMask,
+       /* freeData */          freeData,
+       /* isDisabled */        NULL,
+       /* updateDepgraph */    NULL,
+       /* dependsOnTime */     NULL,
+       /* dependsOnNormals */  NULL,
+       /* foreachObjectLink */ NULL,
+       /* foreachIDLink */     NULL,
+       /* foreachTexLink */    NULL,
+};
index eac2f24d064062c4e2578062738774f658c413e3..be6f7af77918f41b6f8ea9ca0f80de352fefb634 100644 (file)
@@ -306,5 +306,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
        INIT_TYPE(Wireframe);
        INIT_TYPE(DataTransfer);
        INIT_TYPE(NormalEdit);
+       INIT_TYPE(CorrectiveSmooth);
 #undef INIT_TYPE
 }