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