code cleanup: make bmesh operator names more consistant since python has access to...
[blender.git] / source / blender / modifiers / intern / MOD_solidify.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software  Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2005 by the Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Campbell Barton
22  *                 Shinsuke Irie
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  *
26  */
27
28 /** \file blender/modifiers/intern/MOD_solidify.c
29  *  \ingroup modifiers
30  */
31
32 #include "DNA_meshdata_types.h"
33
34 #include "MEM_guardedalloc.h"
35
36 #include "BLI_utildefines.h"
37 #include "BLI_math.h"
38 #include "BLI_edgehash.h"
39 #include "BLI_array.h"
40 #include "BLI_string.h"
41
42 #include "BKE_cdderivedmesh.h"
43 #include "BKE_mesh.h"
44 #include "BKE_particle.h"
45 #include "BKE_deform.h"
46
47 #include "MOD_modifiertypes.h"
48 #include "MOD_util.h"
49
50 /* *** derived mesh high quality normal calculation function  *** */
51 /* could be exposed for other functions to use */
52
53 typedef struct EdgeFaceRef {
54         int f1; /* init as -1 */
55         int f2;
56 } EdgeFaceRef;
57
58 static void dm_calc_normal(DerivedMesh *dm, float (*temp_nors)[3])
59 {
60         int i, numVerts, numEdges, numFaces;
61         MPoly *mpoly, *mp;
62         MLoop *mloop, *ml;
63         MVert *mvert, *mv;
64
65         float (*face_nors)[3];
66         float *f_no;
67         int calc_face_nors = 0;
68
69         numVerts = dm->getNumVerts(dm);
70         numEdges = dm->getNumEdges(dm);
71         numFaces = dm->getNumPolys(dm);
72         mpoly = dm->getPolyArray(dm);
73         mvert = dm->getVertArray(dm);
74         mloop = dm->getLoopArray(dm);
75
76         /* we don't want to overwrite any referenced layers */
77
78         /* Doesn't work here! */
79 #if 0
80         mv = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, numVerts);
81         cddm->mvert = mv;
82 #endif
83
84         face_nors = CustomData_get_layer(&dm->polyData, CD_NORMAL);
85         if (!face_nors) {
86                 calc_face_nors = 1;
87                 face_nors = CustomData_add_layer(&dm->polyData, CD_NORMAL, CD_CALLOC, NULL, numFaces);
88         }
89
90         mv = mvert;
91         mp = mpoly;
92
93         {
94                 EdgeHash *edge_hash = BLI_edgehash_new();
95                 EdgeHashIterator *edge_iter;
96                 int edge_ref_count = 0;
97                 unsigned int ed_v1, ed_v2; /* use when getting the key */
98                 EdgeFaceRef *edge_ref_array = MEM_callocN(numEdges * sizeof(EdgeFaceRef), "Edge Connectivity");
99                 EdgeFaceRef *edge_ref;
100                 float edge_normal[3];
101
102                 /* This loop adds an edge hash if its not there, and adds the face index */
103                 for (i = 0; i < numFaces; i++, mp++) {
104                         unsigned int ml_v1;
105                         unsigned int ml_v2;
106                         int j;
107
108                         f_no = face_nors[i];
109                         if (calc_face_nors)
110                                 BKE_mesh_calc_poly_normal(mp, mloop + mp->loopstart, mvert, f_no);
111
112                         ml = mloop + mp->loopstart;
113
114                         for (j = 0, ml_v1 = ml->v, ml_v2 = ml[mp->totloop - 1].v;
115                              j < mp->totloop;
116                              j++, ml++, ml_v2 = ml_v1, ml_v1 = ml->v)
117                         {
118                                 /* --- add edge ref to face --- */
119                                 edge_ref = (EdgeFaceRef *)BLI_edgehash_lookup(edge_hash, ml_v1, ml_v2);
120                                 if (!edge_ref) {
121                                         edge_ref = &edge_ref_array[edge_ref_count++];
122                                         edge_ref->f1 =  i;
123                                         edge_ref->f2 = -1;
124                                         BLI_edgehash_insert(edge_hash, ml_v1, ml_v2, edge_ref);
125                                 }
126                                 else {
127                                         edge_ref->f2 = i;
128                                 }
129                                 /* --- done --- */
130                         }
131                 }
132
133                 for (edge_iter = BLI_edgehashIterator_new(edge_hash);
134                      !BLI_edgehashIterator_isDone(edge_iter);
135                      BLI_edgehashIterator_step(edge_iter))
136                 {
137                         /* Get the edge vert indices, and edge value (the face indices that use it)*/
138                         BLI_edgehashIterator_getKey(edge_iter, &ed_v1, &ed_v2);
139                         edge_ref = BLI_edgehashIterator_getValue(edge_iter);
140
141                         if (edge_ref->f2 != -1) {
142                                 /* We have 2 faces using this edge, calculate the edges normal
143                                  * using the angle between the 2 faces as a weighting */
144                                 add_v3_v3v3(edge_normal, face_nors[edge_ref->f1], face_nors[edge_ref->f2]);
145                                 normalize_v3(edge_normal);
146                                 mul_v3_fl(edge_normal, angle_normalized_v3v3(face_nors[edge_ref->f1], face_nors[edge_ref->f2]));
147                         }
148                         else {
149                                 /* only one face attached to that edge */
150                                 /* an edge without another attached- the weight on this is
151                                  * undefined, M_PI/2 is 90d in radians and that seems good enough */
152                                 mul_v3_v3fl(edge_normal, face_nors[edge_ref->f1], M_PI / 2);
153                         }
154                         add_v3_v3(temp_nors[ed_v1], edge_normal);
155                         add_v3_v3(temp_nors[ed_v2], edge_normal);
156                 }
157                 BLI_edgehashIterator_free(edge_iter);
158                 BLI_edgehash_free(edge_hash, NULL);
159                 MEM_freeN(edge_ref_array);
160         }
161
162         /* normalize vertex normals and assign */
163         for (i = 0; i < numVerts; i++, mv++) {
164                 if (normalize_v3(temp_nors[i]) == 0.0f) {
165                         normal_short_to_float_v3(temp_nors[i], mv->no);
166                 }
167         }
168 }
169
170 static void initData(ModifierData *md)
171 {
172         SolidifyModifierData *smd = (SolidifyModifierData *) md;
173         smd->offset = 0.01f;
174         smd->offset_fac = -1.0f;
175         smd->flag = MOD_SOLIDIFY_RIM;
176 }
177
178 static void copyData(ModifierData *md, ModifierData *target)
179 {
180         SolidifyModifierData *smd = (SolidifyModifierData *) md;
181         SolidifyModifierData *tsmd = (SolidifyModifierData *) target;
182         tsmd->offset = smd->offset;
183         tsmd->offset_fac = smd->offset_fac;
184         tsmd->crease_inner = smd->crease_inner;
185         tsmd->crease_outer = smd->crease_outer;
186         tsmd->crease_rim = smd->crease_rim;
187         tsmd->flag = smd->flag;
188         BLI_strncpy(tsmd->defgrp_name, smd->defgrp_name, sizeof(tsmd->defgrp_name));
189 }
190
191 static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
192 {
193         SolidifyModifierData *smd = (SolidifyModifierData *) md;
194         CustomDataMask dataMask = 0;
195
196         /* ask for vertexgroups if we need them */
197         if (smd->defgrp_name[0]) dataMask |= CD_MASK_MDEFORMVERT;
198
199         return dataMask;
200 }
201
202 /* specific function for solidify - define locally */
203 BLI_INLINE void madd_v3v3short_fl(float r[3], const short a[3], const float f)
204 {
205         r[0] += (float)a[0] * f;
206         r[1] += (float)a[1] * f;
207         r[2] += (float)a[2] * f;
208 }
209
210 static DerivedMesh *applyModifier(
211         ModifierData *md, Object *ob,
212         DerivedMesh *dm,
213         ModifierApplyFlag UNUSED(flag))
214 {
215         int i;
216         DerivedMesh *result;
217         const SolidifyModifierData *smd = (SolidifyModifierData *) md;
218
219         MVert *mv, *mvert, *orig_mvert;
220         MEdge *ed, *medge, *orig_medge;
221         MLoop *ml, *mloop, *orig_mloop;
222         MPoly *mp, *mpoly, *orig_mpoly;
223         const int numVerts = dm->getNumVerts(dm);
224         const int numEdges = dm->getNumEdges(dm);
225         const int numFaces = dm->getNumPolys(dm);
226         int numLoops = 0, newLoops = 0, newFaces = 0, newEdges = 0;
227
228         /* only use material offsets if we have 2 or more materials  */
229         const short mat_nr_max = ob->totcol > 1 ? ob->totcol - 1 : 0;
230         const short mat_ofs = mat_nr_max ? smd->mat_ofs : 0;
231         const short mat_ofs_rim = mat_nr_max ? smd->mat_ofs_rim : 0;
232
233         /* use for edges */
234         int *new_vert_arr = NULL;
235         BLI_array_declare(new_vert_arr);
236         int *new_edge_arr = NULL;
237         BLI_array_declare(new_edge_arr);
238         int *old_vert_arr = MEM_callocN(sizeof(int) * numVerts, "old_vert_arr in solidify");
239
240         int *edge_users = NULL;
241         char *edge_order = NULL;
242
243         float (*vert_nors)[3] = NULL;
244
245         float (*face_nors_result)[3] = NULL;
246
247         const float ofs_orig = -(((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset);
248         const float ofs_new  = smd->offset + ofs_orig;
249         const float offset_fac_vg = smd->offset_fac_vg;
250         const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg;
251         const int do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0;
252
253         /* weights */
254         MDeformVert *dvert, *dv = NULL;
255         const int defgrp_invert = ((smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0);
256         int defgrp_index;
257
258         modifier_get_vgroup(ob, dm, smd->defgrp_name, &dvert, &defgrp_index);
259
260         numLoops = dm->numLoopData;
261         newLoops = 0;
262
263         orig_mvert = dm->getVertArray(dm);
264         orig_medge = dm->getEdgeArray(dm);
265         orig_mloop = dm->getLoopArray(dm);
266         orig_mpoly = dm->getPolyArray(dm);
267
268         if (smd->flag & MOD_SOLIDIFY_RIM) {
269                 EdgeHash *edgehash = BLI_edgehash_new();
270                 EdgeHashIterator *ehi;
271                 unsigned int v1, v2;
272                 int eidx;
273
274 #define INVALID_UNUSED -1
275 #define INVALID_PAIR -2
276
277                 edge_users = MEM_mallocN(sizeof(int) * numEdges, "solid_mod edges");
278                 edge_order = MEM_mallocN(sizeof(char) * numEdges, "solid_mod eorder");
279
280                 for (i = 0, mv = orig_mvert; i < numVerts; i++, mv++) {
281                         mv->flag &= ~ME_VERT_TMP_TAG;
282                 }
283
284                 /* save doing 2 loops here... */
285 #if 0
286                 fill_vn_i(edge_users, numEdges, INVALID_UNUSED);
287 #endif
288
289                 for (i = 0, ed = orig_medge; i < numEdges; i++, ed++) {
290                         BLI_edgehash_insert(edgehash, ed->v1, ed->v2, SET_INT_IN_POINTER(i));
291                         edge_users[i] = INVALID_UNUSED;
292                 }
293
294                 for (i = 0, mp = orig_mpoly; i < numFaces; i++, mp++) {
295                         unsigned int ml_v1;
296                         unsigned int ml_v2;
297                         int j;
298
299                         ml = orig_mloop + mp->loopstart;
300
301                         for (j = 0, ml_v1 = ml->v, ml_v2 = ml[mp->totloop - 1].v;
302                              j < mp->totloop;
303                              j++, ml++, ml_v2 = ml_v1, ml_v1 = ml->v)
304                         {
305                                 /* add edge user */
306                                 eidx = GET_INT_FROM_POINTER(BLI_edgehash_lookup(edgehash, ml_v1, ml_v2));
307                                 if (edge_users[eidx] == INVALID_UNUSED) {
308                                         ed = orig_medge + eidx;
309                                         edge_users[eidx] = (ml_v1 < ml_v2) == (ed->v1 < ed->v2) ? i : (i + numFaces);
310                                         edge_order[eidx] = j;
311                                 }
312                                 else {
313                                         edge_users[eidx] = INVALID_PAIR;
314                                 }
315                         }
316                 }
317
318 #undef INVALID_UNUSED
319 #undef INVALID_PAIR
320
321                 ehi = BLI_edgehashIterator_new(edgehash);
322                 for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
323                         eidx = GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi));
324                         if (edge_users[eidx] >= 0) {
325                                 BLI_edgehashIterator_getKey(ehi, &v1, &v2);
326                                 orig_mvert[v1].flag |= ME_VERT_TMP_TAG;
327                                 orig_mvert[v2].flag |= ME_VERT_TMP_TAG;
328                                 BLI_array_append(new_edge_arr, eidx);
329                                 newFaces++;
330                                 newLoops += 4;
331                         }
332                 }
333                 BLI_edgehashIterator_free(ehi);
334
335                 for (i = 0, mv = orig_mvert; i < numVerts; i++, mv++) {
336                         if (mv->flag & ME_VERT_TMP_TAG) {
337                                 old_vert_arr[i] = BLI_array_count(new_vert_arr);
338                                 BLI_array_append(new_vert_arr, i);
339                                 newEdges++;
340
341                                 mv->flag &= ~ME_VERT_TMP_TAG;
342                         }
343                 }
344
345                 BLI_edgehash_free(edgehash, NULL);
346         }
347
348         if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) {
349                 vert_nors = MEM_callocN(sizeof(float) * numVerts * 3, "mod_solid_vno_hq");
350                 dm_calc_normal(dm, vert_nors);
351         }
352
353         result = CDDM_from_template(dm, numVerts * 2, (numEdges * 2) + newEdges, 0,
354                                     (numLoops * 2) + newLoops, (numFaces * 2) + newFaces);
355
356         mpoly = CDDM_get_polys(result);
357         mloop = CDDM_get_loops(result);
358         medge = CDDM_get_edges(result);
359         mvert = CDDM_get_verts(result);
360
361         DM_copy_edge_data(dm, result, 0, 0, numEdges);
362         DM_copy_edge_data(dm, result, 0, numEdges, numEdges);
363
364         DM_copy_vert_data(dm, result, 0, 0, numVerts);
365         DM_copy_vert_data(dm, result, 0, numVerts, numVerts);
366
367         DM_copy_loop_data(dm, result, 0, 0, numLoops);
368         DM_copy_loop_data(dm, result, 0, numLoops, numLoops);
369
370         DM_copy_poly_data(dm, result, 0, 0, numFaces);
371         DM_copy_poly_data(dm, result, 0, numFaces, numFaces);
372
373         /* if the original has it, get the result so we can update it */
374         face_nors_result = CustomData_get_layer(&result->polyData, CD_NORMAL);
375
376         /* flip normals */
377         mp = mpoly + numFaces;
378         for (i = 0; i < dm->numPolyData; i++, mp++) {
379                 MLoop *ml2;
380                 int e;
381                 int j;
382
383                 ml2 = mloop + mp->loopstart + dm->numLoopData;
384                 for (j = 0; j < mp->totloop; j++) {
385                         CustomData_copy_data(&dm->loopData, &result->loopData, mp->loopstart + j,
386                                              mp->loopstart + (mp->totloop - j - 1) + dm->numLoopData, 1);
387                 }
388
389                 if (mat_ofs) {
390                         mp->mat_nr += mat_ofs;
391                         CLAMP(mp->mat_nr, 0, mat_nr_max);
392                 }
393
394                 e = ml2[0].e;
395                 for (j = 0; j < mp->totloop - 1; j++) {
396                         ml2[j].e = ml2[j + 1].e;
397                 }
398                 ml2[mp->totloop - 1].e = e;
399
400                 mp->loopstart += dm->numLoopData;
401
402                 for (j = 0; j < mp->totloop; j++) {
403                         ml2[j].e += numEdges;
404                         ml2[j].v += numVerts;
405                 }
406
407                 if (face_nors_result) {
408                         negate_v3_v3(face_nors_result[numFaces + i], face_nors_result[i]);
409                 }
410         }
411
412         for (i = 0, ed = medge + numEdges; i < numEdges; i++, ed++) {
413                 ed->v1 += numVerts;
414                 ed->v2 += numVerts;
415         }
416
417         /* note, copied vertex layers don't have flipped normals yet. do this after applying offset */
418         if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) {
419                 /* no even thickness, very simple */
420                 float scalar_short;
421                 float scalar_short_vgroup;
422
423
424                 if (ofs_new != 0.0f) {
425                         scalar_short = scalar_short_vgroup = ofs_new / 32767.0f;
426                         mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? numVerts : 0);
427                         dv = dvert;
428                         for (i = 0; i < numVerts; i++, mv++) {
429                                 if (dv) {
430                                         if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index);
431                                         else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index);
432                                         scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short;
433                                         dv++;
434                                 }
435                                 madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup);
436                         }
437                 }
438
439                 if (ofs_orig != 0.0f) {
440                         scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f;
441                         mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? 0 : numVerts); /* as above but swapped */
442                         dv = dvert;
443                         for (i = 0; i < numVerts; i++, mv++) {
444                                 if (dv) {
445                                         if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index);
446                                         else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index);
447                                         scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short;
448                                         dv++;
449                                 }
450                                 madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup);
451                         }
452                 }
453         }
454         else {
455                 /* make a face normal layer if not present */
456                 float (*face_nors)[3];
457                 int face_nors_calc = 0;
458
459                 /* same as EM_solidify() in editmesh_lib.c */
460                 float *vert_angles = MEM_callocN(sizeof(float) * numVerts * 2, "mod_solid_pair"); /* 2 in 1 */
461                 float *vert_accum = vert_angles + numVerts;
462                 int vidx;
463
464                 face_nors = CustomData_get_layer(&dm->polyData, CD_NORMAL);
465                 if (!face_nors) {
466                         face_nors = CustomData_add_layer(&dm->polyData, CD_NORMAL, CD_CALLOC, NULL, dm->numPolyData);
467                         face_nors_calc = 1;
468                 }
469
470                 if (vert_nors == NULL) {
471                         vert_nors = MEM_mallocN(sizeof(float) * numVerts * 3, "mod_solid_vno");
472                         for (i = 0, mv = mvert; i < numVerts; i++, mv++) {
473                                 normal_short_to_float_v3(vert_nors[i], mv->no);
474                         }
475                 }
476
477                 for (i = 0, mp = mpoly; i < numFaces; i++, mp++) {
478                         /* #BKE_mesh_poly_calc_angles logic is inlined here */
479                         float nor_prev[3];
480                         float nor_next[3];
481
482                         int i_this = mp->totloop - 1;
483                         int i_next = 0;
484
485                         ml = &mloop[mp->loopstart];
486
487                         /* --- not related to angle calc --- */
488                         if (face_nors_calc)
489                                 BKE_mesh_calc_poly_normal(mp, ml, mvert, face_nors[i]);
490                         /* --- end non-angle-calc section --- */
491
492                         sub_v3_v3v3(nor_prev, mvert[ml[i_this - 1].v].co, mvert[ml[i_this].v].co);
493                         normalize_v3(nor_prev);
494
495                         while (i_next < mp->totloop) {
496                                 float angle;
497                                 sub_v3_v3v3(nor_next, mvert[ml[i_this].v].co, mvert[ml[i_next].v].co);
498                                 normalize_v3(nor_next);
499                                 angle = angle_normalized_v3v3(nor_prev, nor_next);
500
501
502                                 /* --- not related to angle calc --- */
503                                 if (angle < FLT_EPSILON) {
504                                         angle = FLT_EPSILON;
505                                 }
506                                 vidx = ml[i_this].v;
507                                 vert_accum[vidx] += angle;
508                                 vert_angles[vidx] += shell_angle_to_dist(angle_normalized_v3v3(vert_nors[vidx], face_nors[i])) * angle;
509                                 /* --- end non-angle-calc section --- */
510
511
512                                 /* step */
513                                 copy_v3_v3(nor_prev, nor_next);
514                                 i_this = i_next;
515                                 i_next++;
516                         }
517                 }
518
519                 /* vertex group support */
520                 if (dvert) {
521                         float scalar;
522
523                         dv = dvert;
524                         if (defgrp_invert) {
525                                 for (i = 0; i < numVerts; i++, dv++) {
526                                         scalar = 1.0f - defvert_find_weight(dv, defgrp_index);
527                                         scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
528                                         vert_angles[i] *= scalar;
529                                 }
530                         }
531                         else {
532                                 for (i = 0; i < numVerts; i++, dv++) {
533                                         scalar = defvert_find_weight(dv, defgrp_index);
534                                         scalar = offset_fac_vg + (scalar * offset_fac_vg_inv);
535                                         vert_angles[i] *= scalar;
536                                 }
537                         }
538                 }
539
540                 if (ofs_new) {
541                         mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? numVerts : 0);
542
543                         for (i = 0; i < numVerts; i++, mv++) {
544                                 if (vert_accum[i]) { /* zero if unselected */
545                                         madd_v3_v3fl(mv->co, vert_nors[i], ofs_new * (vert_angles[i] / vert_accum[i]));
546                                 }
547                         }
548                 }
549
550                 if (ofs_orig) {
551                         /* same as above but swapped, intentional use of 'ofs_new' */
552                         mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? 0 : numVerts);
553
554                         for (i = 0; i < numVerts; i++, mv++) {
555                                 if (vert_accum[i]) { /* zero if unselected */
556                                         madd_v3_v3fl(mv->co, vert_nors[i], ofs_orig * (vert_angles[i] / vert_accum[i]));
557                                 }
558                         }
559                 }
560
561                 MEM_freeN(vert_angles);
562         }
563
564         if (vert_nors)
565                 MEM_freeN(vert_nors);
566
567         /* flip vertex normals for copied verts */
568         mv = mvert + numVerts;
569         for (i = 0; i < numVerts; i++, mv++) {
570                 negate_v3_short(mv->no);
571         }
572
573         if (smd->flag & MOD_SOLIDIFY_RIM) {
574
575                 /* bugger, need to re-calculate the normals for the new edge faces.
576                  * This could be done in many ways, but probably the quickest way
577                  * is to calculate the average normals for side faces only.
578                  * Then blend them with the normals of the edge verts.
579                  *
580                  * at the moment its easiest to allocate an entire array for every vertex,
581                  * even though we only need edge verts - campbell
582                  */
583
584 #define SOLIDIFY_SIDE_NORMALS
585
586 #ifdef SOLIDIFY_SIDE_NORMALS
587                 /* annoying to allocate these since we only need the edge verts, */
588                 float (*edge_vert_nos)[3] = MEM_callocN(sizeof(float) * numVerts * 3, "solidify_edge_nos");
589                 float nor[3];
590 #endif
591                 const unsigned char crease_rim = smd->crease_rim * 255.0f;
592                 const unsigned char crease_outer = smd->crease_outer * 255.0f;
593                 const unsigned char crease_inner = smd->crease_inner * 255.0f;
594
595                 int *origindex_edge;
596                 int *orig_ed;
597                 int j;
598
599                 /* add faces & edges */
600                 origindex_edge = result->getEdgeDataArray(result, CD_ORIGINDEX);
601                 ed = &medge[numEdges * 2];
602                 orig_ed = &origindex_edge[numEdges * 2];
603                 for (i = 0; i < newEdges; i++, ed++, orig_ed++) {
604                         ed->v1 = new_vert_arr[i];
605                         ed->v2 = new_vert_arr[i] + numVerts;
606                         ed->flag |= ME_EDGEDRAW;
607
608                         *orig_ed = ORIGINDEX_NONE;
609
610                         if (crease_rim) {
611                                 ed->crease = crease_rim;
612                         }
613                 }
614
615                 /* faces */
616                 mp = mpoly + (numFaces * 2);
617                 ml = mloop + (numLoops * 2);
618                 j = 0;
619                 for (i = 0; i < newFaces; i++, mp++) {
620                         int eidx = new_edge_arr[i];
621                         int fidx = edge_users[eidx];
622                         int flip, k1, k2;
623
624                         if (fidx >= numFaces) {
625                                 fidx -= numFaces;
626                                 flip = TRUE;
627                         }
628                         else {
629                                 flip = FALSE;
630                         }
631
632                         ed = medge + eidx;
633
634                         /* copy most of the face settings */
635                         DM_copy_poly_data(dm, result, fidx, (numFaces * 2) + i, 1);
636                         mp->loopstart = j + numLoops * 2;
637                         mp->flag = mpoly[fidx].flag;
638
639                         /* notice we use 'mp->totloop' which is later overwritten,
640                          * we could lookup the original face but theres no point since this is a copy
641                          * and will have the same value, just take care when changing order of assignment */
642                         k1 = mpoly[fidx].loopstart + (((edge_order[eidx] - 1) + mp->totloop) % mp->totloop);  /* prev loop */
643                         k2 = mpoly[fidx].loopstart +  (edge_order[eidx]);
644
645                         mp->totloop = 4;
646
647                         CustomData_copy_data(&dm->loopData, &result->loopData, k2, numLoops * 2 + j + 0, 1);
648                         CustomData_copy_data(&dm->loopData, &result->loopData, k1, numLoops * 2 + j + 1, 1);
649                         CustomData_copy_data(&dm->loopData, &result->loopData, k1, numLoops * 2 + j + 2, 1);
650                         CustomData_copy_data(&dm->loopData, &result->loopData, k2, numLoops * 2 + j + 3, 1);
651
652                         if (flip == FALSE) {
653                                 ml[j].v = ed->v1;
654                                 ml[j++].e = eidx;
655
656                                 ml[j].v = ed->v2;
657                                 ml[j++].e = numEdges * 2 + old_vert_arr[ed->v2];
658
659                                 ml[j].v = ed->v2 + numVerts;
660                                 ml[j++].e = eidx + numEdges;
661
662                                 ml[j].v = ed->v1 + numVerts;
663                                 ml[j++].e = numEdges * 2 + old_vert_arr[ed->v1];
664                         }
665                         else {
666                                 ml[j].v = ed->v2;
667                                 ml[j++].e = eidx;
668
669                                 ml[j].v = ed->v1;
670                                 ml[j++].e = numEdges * 2 + old_vert_arr[ed->v1];
671
672                                 ml[j].v = ed->v1 + numVerts;
673                                 ml[j++].e = eidx + numEdges;
674
675                                 ml[j].v = ed->v2 + numVerts;
676                                 ml[j++].e = numEdges * 2 + old_vert_arr[ed->v2];
677                         }
678
679                         origindex_edge[ml[j - 3].e] = ORIGINDEX_NONE;
680                         origindex_edge[ml[j - 1].e] = ORIGINDEX_NONE;
681
682                         /* use the next material index if option enabled */
683                         if (mat_ofs_rim) {
684                                 mp->mat_nr += mat_ofs_rim;
685                                 CLAMP(mp->mat_nr, 0, mat_nr_max);
686                         }
687                         if (crease_outer) {
688                                 /* crease += crease_outer; without wrapping */
689                                 unsigned char *cr = (unsigned char *)&(ed->crease);
690                                 int tcr = *cr + crease_outer;
691                                 *cr = tcr > 255 ? 255 : tcr;
692                         }
693
694                         if (crease_inner) {
695                                 /* crease += crease_inner; without wrapping */
696                                 unsigned char *cr = (unsigned char *)&(medge[numEdges + eidx].crease);
697                                 int tcr = *cr + crease_inner;
698                                 *cr = tcr > 255 ? 255 : tcr;
699                         }
700
701 #ifdef SOLIDIFY_SIDE_NORMALS
702                         normal_quad_v3(nor,
703                                        mvert[ml[j - 4].v].co,
704                                        mvert[ml[j - 3].v].co,
705                                        mvert[ml[j - 2].v].co,
706                                        mvert[ml[j - 1].v].co);
707
708                         add_v3_v3(edge_vert_nos[ed->v1], nor);
709                         add_v3_v3(edge_vert_nos[ed->v2], nor);
710
711                         if (face_nors_result) {
712                                 copy_v3_v3(face_nors_result[(numFaces * 2) + i], nor);
713                         }
714 #endif
715                 }
716
717 #ifdef SOLIDIFY_SIDE_NORMALS
718                 ed = medge + (numEdges * 2);
719                 for (i = 0; i < newEdges; i++, ed++) {
720                         float nor_cpy[3];
721                         short *nor_short;
722                         int k;
723
724                         /* note, only the first vertex (lower half of the index) is calculated */
725                         normalize_v3_v3(nor_cpy, edge_vert_nos[ed->v1]);
726
727                         for (k = 0; k < 2; k++) { /* loop over both verts of the edge */
728                                 nor_short = mvert[*(&ed->v1 + k)].no;
729                                 normal_short_to_float_v3(nor, nor_short);
730                                 add_v3_v3(nor, nor_cpy);
731                                 normalize_v3(nor);
732                                 normal_float_to_short_v3(nor_short, nor);
733                         }
734                 }
735
736                 MEM_freeN(edge_vert_nos);
737 #endif
738
739                 BLI_array_free(new_vert_arr);
740                 BLI_array_free(new_edge_arr);
741                 MEM_freeN(edge_users);
742                 MEM_freeN(edge_order);
743         }
744
745         if (old_vert_arr)
746                 MEM_freeN(old_vert_arr);
747
748         /* must recalculate normals with vgroups since they can displace unevenly [#26888] */
749         if (dvert) {
750                 CDDM_calc_normals(result);
751         }
752
753         return result;
754 }
755
756 #undef SOLIDIFY_SIDE_NORMALS
757
758 static DerivedMesh *applyModifierEM(ModifierData *md,
759                                     Object *ob,
760                                     struct BMEditMesh *UNUSED(editData),
761                                     DerivedMesh *derivedData)
762 {
763         return applyModifier(md, ob, derivedData, MOD_APPLY_USECACHE);
764 }
765
766
767 ModifierTypeInfo modifierType_Solidify = {
768         /* name */              "Solidify",
769         /* structName */        "SolidifyModifierData",
770         /* structSize */        sizeof(SolidifyModifierData),
771         /* type */              eModifierTypeType_Constructive,
772
773         /* flags */             eModifierTypeFlag_AcceptsMesh |
774                                 eModifierTypeFlag_AcceptsCVs |
775                                 eModifierTypeFlag_SupportsMapping |
776                                 eModifierTypeFlag_SupportsEditmode |
777                                 eModifierTypeFlag_EnableInEditmode,
778
779         /* copyData */          copyData,
780         /* deformVerts */       NULL,
781         /* deformMatrices */    NULL,
782         /* deformVertsEM */     NULL,
783         /* deformMatricesEM */  NULL,
784         /* applyModifier */     applyModifier,
785         /* applyModifierEM */   applyModifierEM,
786         /* initData */          initData,
787         /* requiredDataMask */  requiredDataMask,
788         /* freeData */          NULL,
789         /* isDisabled */        NULL,
790         /* updateDepgraph */    NULL,
791         /* dependsOnTime */     NULL,
792         /* dependsOnNormals */  NULL,
793         /* foreachObjectLink */ NULL,
794         /* foreachIDLink */     NULL,
795         /* foreachTexLink */    NULL,
796 };