/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2005 by the Blender Foundation. * All rights reserved. * * Contributor(s): Daniel Dunbar * Ton Roosendaal, * Ben Batt, * Brecht Van Lommel, * Campbell Barton * * ***** END GPL LICENSE BLOCK ***** * */ #include "DNA_meshdata_types.h" #include "BLI_math.h" #include "BLI_edgehash.h" #include "BKE_cdderivedmesh.h" #include "BKE_mesh.h" #include "BKE_particle.h" #include "BKE_deform.h" #include "BKE_utildefines.h" #include "MOD_modifiertypes.h" #include "MEM_guardedalloc.h" typedef struct EdgeFaceRef { int f1; /* init as -1 */ int f2; } EdgeFaceRef; static void dm_calc_normal(DerivedMesh *dm, float (*temp_nors)[3]) { int i, numVerts, numEdges, numFaces; MFace *mface, *mf; MVert *mvert, *mv; float (*face_nors)[3]; float *f_no; int calc_face_nors= 0; numVerts = dm->getNumVerts(dm); numEdges = dm->getNumEdges(dm); numFaces = dm->getNumFaces(dm); mface = dm->getFaceArray(dm); mvert = dm->getVertArray(dm); /* we don't want to overwrite any referenced layers */ /* Dosnt work here! mv = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT); cddm->mvert = mv; */ face_nors = CustomData_get_layer(&dm->faceData, CD_NORMAL); if(!face_nors) { calc_face_nors = 1; face_nors = CustomData_add_layer(&dm->faceData, CD_NORMAL, CD_CALLOC, NULL, numFaces); } mv = mvert; mf = mface; { EdgeHash *edge_hash = BLI_edgehash_new(); EdgeHashIterator *edge_iter; int edge_ref_count = 0; int ed_v1, ed_v2; /* use when getting the key */ EdgeFaceRef *edge_ref_array = MEM_callocN(numEdges * sizeof(EdgeFaceRef), "Edge Connectivity"); EdgeFaceRef *edge_ref; float edge_normal[3]; /* This function adds an edge hash if its not there, and adds the face index */ #define NOCALC_EDGEWEIGHT_ADD_EDGEREF_FACE(EDV1, EDV2); \ edge_ref = (EdgeFaceRef *)BLI_edgehash_lookup(edge_hash, EDV1, EDV2); \ if (!edge_ref) { \ edge_ref = &edge_ref_array[edge_ref_count]; edge_ref_count++; \ edge_ref->f1=i; \ edge_ref->f2=-1; \ BLI_edgehash_insert(edge_hash, EDV1, EDV2, edge_ref); \ } else { \ edge_ref->f2=i; \ } for(i = 0; i < numFaces; i++, mf++) { f_no = face_nors[i]; if(mf->v4) { if(calc_face_nors) normal_quad_v3(f_no, mv[mf->v1].co, mv[mf->v2].co, mv[mf->v3].co, mv[mf->v4].co); NOCALC_EDGEWEIGHT_ADD_EDGEREF_FACE(mf->v1, mf->v2); NOCALC_EDGEWEIGHT_ADD_EDGEREF_FACE(mf->v2, mf->v3); NOCALC_EDGEWEIGHT_ADD_EDGEREF_FACE(mf->v3, mf->v4); NOCALC_EDGEWEIGHT_ADD_EDGEREF_FACE(mf->v4, mf->v1); } else { if(calc_face_nors) normal_tri_v3(f_no, mv[mf->v1].co, mv[mf->v2].co, mv[mf->v3].co); NOCALC_EDGEWEIGHT_ADD_EDGEREF_FACE(mf->v1, mf->v2); NOCALC_EDGEWEIGHT_ADD_EDGEREF_FACE(mf->v2, mf->v3); NOCALC_EDGEWEIGHT_ADD_EDGEREF_FACE(mf->v3, mf->v1); } } for(edge_iter = BLI_edgehashIterator_new(edge_hash); !BLI_edgehashIterator_isDone(edge_iter); BLI_edgehashIterator_step(edge_iter)) { /* Get the edge vert indicies, and edge value (the face indicies that use it)*/ BLI_edgehashIterator_getKey(edge_iter, (int*)&ed_v1, (int*)&ed_v2); edge_ref = BLI_edgehashIterator_getValue(edge_iter); if (edge_ref->f2 != -1) { /* We have 2 faces using this edge, calculate the edges normal * using the angle between the 2 faces as a weighting */ add_v3_v3v3(edge_normal, face_nors[edge_ref->f1], face_nors[edge_ref->f2]); normalize_v3(edge_normal); mul_v3_fl(edge_normal, angle_normalized_v3v3(face_nors[edge_ref->f1], face_nors[edge_ref->f2])); } else { /* only one face attached to that edge */ /* an edge without another attached- the weight on this is * undefined, M_PI/2 is 90d in radians and that seems good enough */ mul_v3_v3fl(edge_normal, face_nors[edge_ref->f1], M_PI/2); } add_v3_v3(temp_nors[ed_v1], edge_normal); add_v3_v3(temp_nors[ed_v2], edge_normal); } BLI_edgehashIterator_free(edge_iter); BLI_edgehash_free(edge_hash, NULL); MEM_freeN(edge_ref_array); } /* normalize vertex normals and assign */ for(i = 0; i < numVerts; i++, mv++) { if(normalize_v3(temp_nors[i]) == 0.0f) { normal_short_to_float_v3(temp_nors[i], mv->no); } } } static void initData(ModifierData *md) { SolidifyModifierData *smd = (SolidifyModifierData*) md; smd->offset = 0.01f; smd->offset_fac = -1.0f; smd->flag = MOD_SOLIDIFY_RIM; } static void copyData(ModifierData *md, ModifierData *target) { SolidifyModifierData *smd = (SolidifyModifierData*) md; SolidifyModifierData *tsmd = (SolidifyModifierData*) target; tsmd->offset = smd->offset; tsmd->offset_fac = smd->offset_fac; tsmd->crease_inner = smd->crease_inner; tsmd->crease_outer = smd->crease_outer; tsmd->crease_rim = smd->crease_rim; tsmd->flag = smd->flag; strcpy(tsmd->defgrp_name, smd->defgrp_name); } static CustomDataMask requiredDataMask(Object *ob, ModifierData *md) { SolidifyModifierData *smd = (SolidifyModifierData*) md; CustomDataMask dataMask = 0; /* ask for vertexgroups if we need them */ if(smd->defgrp_name[0]) dataMask |= (1 << CD_MDEFORMVERT); return dataMask; } static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc) { int i; DerivedMesh *result; SolidifyModifierData *smd = (SolidifyModifierData*) md; MFace *mf, *mface, *orig_mface; MEdge *ed, *medge, *orig_medge; MVert *mv, *mvert, *orig_mvert; int numVerts = dm->getNumVerts(dm); int numEdges = dm->getNumEdges(dm); int numFaces = dm->getNumFaces(dm); /* use for edges */ int *new_vert_arr= NULL; int newFaces = 0; int *new_edge_arr= NULL; int newEdges = 0; int *edge_users= NULL; char *edge_order= NULL; float (*vert_nors)[3]= NULL; float ofs_orig= - (((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset); float ofs_new= smd->offset - (((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset); /* weights */ MDeformVert *dvert= NULL, *dv= NULL; int defgrp_invert = ((smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0); int defgrp_index= defgroup_name_index(ob, smd->defgrp_name); if (defgrp_index >= 0) dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); orig_mface = dm->getFaceArray(dm); orig_medge = dm->getEdgeArray(dm); orig_mvert = dm->getVertArray(dm); if(smd->flag & MOD_SOLIDIFY_RIM) { EdgeHash *edgehash = BLI_edgehash_new(); EdgeHashIterator *ehi; int v1, v2; int eidx; for(i=0, mv=orig_mvert; iflag &= ~ME_VERT_TMP_TAG; } for(i=0, ed=orig_medge; iv1, ed->v2, SET_INT_IN_POINTER(i)); } #define INVALID_UNUSED -1 #define INVALID_PAIR -2 #define ADD_EDGE_USER(_v1, _v2, edge_ord) \ eidx= GET_INT_FROM_POINTER(BLI_edgehash_lookup(edgehash, _v1, _v2)); \ if(edge_users[eidx] == INVALID_UNUSED) { \ ed= orig_medge + eidx; \ edge_users[eidx]= (_v1 < _v2) == (ed->v1 < ed->v2) ? i:(i+numFaces); \ edge_order[eidx]= edge_ord; \ } else { \ edge_users[eidx]= INVALID_PAIR; \ } \ edge_users= MEM_mallocN(sizeof(int) * numEdges, "solid_mod edges"); edge_order= MEM_mallocN(sizeof(char) * numEdges, "solid_mod eorder"); memset(edge_users, INVALID_UNUSED, sizeof(int) * numEdges); for(i=0, mf=orig_mface; iv4) { ADD_EDGE_USER(mf->v1, mf->v2, 0); ADD_EDGE_USER(mf->v2, mf->v3, 1); ADD_EDGE_USER(mf->v3, mf->v4, 2); ADD_EDGE_USER(mf->v4, mf->v1, 3); } else { ADD_EDGE_USER(mf->v1, mf->v2, 0); ADD_EDGE_USER(mf->v2, mf->v3, 1); ADD_EDGE_USER(mf->v3, mf->v1, 2); } } #undef ADD_EDGE_USER #undef INVALID_UNUSED #undef INVALID_PAIR new_edge_arr= MEM_callocN(sizeof(int) * numEdges, "solid_mod arr"); ehi= BLI_edgehashIterator_new(edgehash); for(; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { int eidx= GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi)); if(edge_users[eidx] >= 0) { BLI_edgehashIterator_getKey(ehi, &v1, &v2); orig_mvert[v1].flag |= ME_VERT_TMP_TAG; orig_mvert[v2].flag |= ME_VERT_TMP_TAG; new_edge_arr[newFaces]= eidx; newFaces++; } } BLI_edgehashIterator_free(ehi); new_vert_arr= MEM_callocN(sizeof(int) * numVerts, "solid_mod new_varr"); for(i=0, mv=orig_mvert; iflag & ME_VERT_TMP_TAG) { new_vert_arr[newEdges] = i; newEdges++; mv->flag &= ~ME_VERT_TMP_TAG; } } BLI_edgehash_free(edgehash, NULL); } if(smd->flag & MOD_SOLIDIFY_NORMAL_CALC) { vert_nors= MEM_callocN(sizeof(float) * numVerts * 3, "mod_solid_vno_hq"); dm_calc_normal(dm, vert_nors); } result = CDDM_from_template(dm, numVerts * 2, (numEdges * 2) + newEdges, (numFaces * 2) + newFaces); mface = result->getFaceArray(result); medge = result->getEdgeArray(result); mvert = result->getVertArray(result); DM_copy_face_data(dm, result, 0, 0, numFaces); DM_copy_face_data(dm, result, 0, numFaces, numFaces); DM_copy_edge_data(dm, result, 0, 0, numEdges); DM_copy_edge_data(dm, result, 0, numEdges, numEdges); DM_copy_vert_data(dm, result, 0, 0, numVerts); DM_copy_vert_data(dm, result, 0, numVerts, numVerts); { static int corner_indices[4] = {2, 1, 0, 3}; int is_quad; for(i=0, mf=mface+numFaces; iv1 += numVerts; mf->v2 += numVerts; mf->v3 += numVerts; if(mf->v4) mf->v4 += numVerts; /* Flip face normal */ { is_quad = mf->v4; SWAP(int, mf->v1, mf->v3); DM_swap_face_data(result, i+numFaces, corner_indices); test_index_face(mf, &result->faceData, numFaces, is_quad ? 4:3); } } } for(i=0, ed=medge+numEdges; iv1 += numVerts; ed->v2 += numVerts; } /* note, copied vertex layers dont have flipped normals yet. do this after applying offset */ if((smd->flag & MOD_SOLIDIFY_EVEN) == 0) { /* no even thickness, very simple */ float scalar_short; float scalar_short_vgroup; if(ofs_new != 0.0f) { scalar_short= scalar_short_vgroup= ofs_new / 32767.0f; mv= mvert + ((ofs_new >= ofs_orig) ? 0 : numVerts); dv= dvert; for(i=0; ico, mv->co, mv->no, scalar_short_vgroup); } } if(ofs_orig != 0.0f) { scalar_short= scalar_short_vgroup= ofs_orig / 32767.0f; mv= mvert + ((ofs_new >= ofs_orig) ? numVerts : 0); /* same as above but swapped, intentional use of 'ofs_new' */ dv= dvert; for(i=0; ico, mv->co, mv->no, scalar_short_vgroup); } } } else { /* make a face normal layer if not present */ float (*face_nors)[3]; int face_nors_calc= 0; /* same as EM_solidify() in editmesh_lib.c */ float *vert_angles= MEM_callocN(sizeof(float) * numVerts * 2, "mod_solid_pair"); /* 2 in 1 */ float *vert_accum= vert_angles + numVerts; float face_angles[4]; int i, j, vidx; face_nors = CustomData_get_layer(&dm->faceData, CD_NORMAL); if(!face_nors) { face_nors = CustomData_add_layer(&dm->faceData, CD_NORMAL, CD_CALLOC, NULL, dm->numFaceData); face_nors_calc= 1; } if(vert_nors==NULL) { vert_nors= MEM_mallocN(sizeof(float) * numVerts * 3, "mod_solid_vno"); for(i=0, mv=mvert; ino); } } for(i=0, mf=mface; iv4) normal_quad_v3(face_nors[i], mvert[mf->v1].co, mvert[mf->v2].co, mvert[mf->v3].co, mvert[mf->v4].co); else normal_tri_v3(face_nors[i] , mvert[mf->v1].co, mvert[mf->v2].co, mvert[mf->v3].co); } if(mf->v4) { angle_quad_v3(face_angles, mvert[mf->v1].co, mvert[mf->v2].co, mvert[mf->v3].co, mvert[mf->v4].co); j= 3; } else { angle_tri_v3(face_angles, mvert[mf->v1].co, mvert[mf->v2].co, mvert[mf->v3].co); j= 2; } for(; j>=0; j--) { vidx = *(&mf->v1 + j); vert_accum[vidx] += face_angles[j]; vert_angles[vidx]+= shell_angle_to_dist(angle_normalized_v3v3(vert_nors[vidx], face_nors[i])) * face_angles[j]; } } /* vertex group support */ if(dvert) { dv= dvert; if(defgrp_invert) { for(i=0; i= ofs_orig) ? 0 : numVerts); for(i=0; ico, vert_nors[i], ofs_new * (vert_angles[i] / vert_accum[i])); } } } if(ofs_orig) { mv= mvert + ((ofs_new >= ofs_orig) ? numVerts : 0); /* same as above but swapped, intentional use of 'ofs_new' */ for(i=0; ico, vert_nors[i], ofs_orig * (vert_angles[i] / vert_accum[i])); } } } MEM_freeN(vert_angles); } if(vert_nors) MEM_freeN(vert_nors); /* flip vertex normals for copied verts */ mv= mvert + numVerts; for(i=0; ino[0]= -mv->no[0]; mv->no[1]= -mv->no[1]; mv->no[2]= -mv->no[2]; } if(smd->flag & MOD_SOLIDIFY_RIM) { /* bugger, need to re-calculate the normals for the new edge faces. * This could be done in many ways, but probably the quickest way is to calculate the average normals for side faces only. * Then blend them with the normals of the edge verts. * * at the moment its easiest to allocate an entire array for every vertex, even though we only need edge verts - campbell */ #define SOLIDIFY_SIDE_NORMALS #ifdef SOLIDIFY_SIDE_NORMALS /* annoying to allocate these since we only need the edge verts, */ float (*edge_vert_nos)[3]= MEM_callocN(sizeof(float) * numVerts * 3, "solidify_edge_nos"); float nor[3]; #endif /* maximum value -1, so we have room to increase */ const short mat_nr_shift= (smd->flag & MOD_SOLIDIFY_RIM_MATERIAL) ? ob->totcol-1 : -1; const unsigned char crease_rim= smd->crease_rim * 255.0f; const unsigned char crease_outer= smd->crease_outer * 255.0f; const unsigned char crease_inner= smd->crease_inner * 255.0f; const int edge_indices[4][4] = { {1, 0, 0, 1}, {2, 1, 1, 2}, {3, 2, 2, 3}, {0, 3, 3, 0}}; /* add faces & edges */ ed= medge + (numEdges * 2); for(i=0; iv1= new_vert_arr[i]; ed->v2= new_vert_arr[i] + numVerts; ed->flag |= ME_EDGEDRAW; if(crease_rim) ed->crease= crease_rim; } /* faces */ mf= mface + (numFaces * 2); for(i=0; i= numFaces) { fidx -= numFaces; flip= 1; } else { flip= 0; } ed= medge + eidx; /* copy most of the face settings */ DM_copy_face_data(dm, result, fidx, (numFaces * 2) + i, 1); if(flip) { DM_swap_face_data(result, (numFaces * 2) + i, edge_indices[edge_order[eidx]]); mf->v1= ed->v1; mf->v2= ed->v2; mf->v3= ed->v2 + numVerts; mf->v4= ed->v1 + numVerts; } else { DM_swap_face_data(result, (numFaces * 2) + i, edge_indices[edge_order[eidx]]); mf->v1= ed->v2; mf->v2= ed->v1; mf->v3= ed->v1 + numVerts; mf->v4= ed->v2 + numVerts; } /* use the next material index if option enabled */ if(mf->mat_nr < mat_nr_shift) mf->mat_nr++; if(crease_outer) ed->crease= crease_outer; if(crease_inner) { medge[numEdges + eidx].crease= crease_inner; } #ifdef SOLIDIFY_SIDE_NORMALS normal_quad_v3(nor, mvert[mf->v1].co, mvert[mf->v2].co, mvert[mf->v3].co, mvert[mf->v4].co); add_v3_v3(edge_vert_nos[ed->v1], nor); add_v3_v3(edge_vert_nos[ed->v2], nor); #endif } #ifdef SOLIDIFY_SIDE_NORMALS ed= medge + (numEdges * 2); for(i=0; iv1]); for(j=0; j<2; j++) { /* loop over both verts of the edge */ nor_short= mvert[*(&ed->v1 + j)].no; normal_short_to_float_v3(nor, nor_short); add_v3_v3(nor, nor_cpy); normalize_v3(nor); normal_float_to_short_v3(nor_short, nor); } } MEM_freeN(edge_vert_nos); #endif MEM_freeN(new_vert_arr); MEM_freeN(new_edge_arr); MEM_freeN(edge_users); MEM_freeN(edge_order); } return result; } #undef SOLIDIFY_SIDE_NORMALS static DerivedMesh *applyModifierEM(ModifierData *md, Object *ob, struct EditMesh *editData, DerivedMesh *derivedData) { return applyModifier(md, ob, derivedData, 0, 1); } ModifierTypeInfo modifierType_Solidify = { /* name */ "Solidify", /* structName */ "SolidifyModifierData", /* structSize */ sizeof(SolidifyModifierData), /* type */ eModifierTypeType_Constructive, /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs | eModifierTypeFlag_SupportsMapping | eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode, /* copyData */ copyData, /* deformVerts */ 0, /* deformVertsEM */ 0, /* deformMatricesEM */ 0, /* applyModifier */ applyModifier, /* applyModifierEM */ applyModifierEM, /* initData */ initData, /* requiredDataMask */ requiredDataMask, /* freeData */ 0, /* isDisabled */ 0, /* updateDepgraph */ 0, /* dependsOnTime */ 0, /* foreachObjectLink */ 0, /* foreachIDLink */ 0, };