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