static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, ModifierApplyFlag UNUSED(flag)) { MaskModifierData *mmd = (MaskModifierData *)md; DerivedMesh *dm = derivedData, *result = NULL; GHash *vertHash = NULL, *edgeHash, *polyHash; GHashIterator *hashIter; MDeformVert *dvert = NULL, *dv; int numPolys = 0, numLoops = 0, numEdges = 0, numVerts = 0; int maxVerts, maxEdges, maxPolys; int i; MPoly *mpoly; MLoop *mloop; MPoly *mpoly_new; MLoop *mloop_new; MEdge *medge_new; MVert *mvert_new; int *loop_mapping; /* Overview of Method: * 1. Get the vertices that are in the vertexgroup of interest * 2. Filter out unwanted geometry (i.e. not in vertexgroup), by populating mappings with new vs old indices * 3. Make a new mesh containing only the mapping data */ /* get original number of verts, edges, and faces */ maxVerts = dm->getNumVerts(dm); maxEdges = dm->getNumEdges(dm); maxPolys = dm->getNumPolys(dm); /* check if we can just return the original mesh * - must have verts and therefore verts assigned to vgroups to do anything useful */ if (!(ELEM(mmd->mode, MOD_MASK_MODE_ARM, MOD_MASK_MODE_VGROUP)) || (maxVerts == 0) || (ob->defbase.first == NULL) ) { return derivedData; } /* if mode is to use selected armature bones, aggregate the bone groups */ if (mmd->mode == MOD_MASK_MODE_ARM) { /* --- using selected bones --- */ Object *oba = mmd->ob_arm; bPoseChannel *pchan; bDeformGroup *def; char *bone_select_array; int bone_select_tot = 0; const int defbase_tot = BLI_countlist(&ob->defbase); /* check that there is armature object with bones to use, otherwise return original mesh */ if (ELEM3(NULL, oba, oba->pose, ob->defbase.first)) return derivedData; /* determine whether each vertexgroup is associated with a selected bone or not * - each cell is a boolean saying whether bone corresponding to the ith group is selected * - groups that don't match a bone are treated as not existing (along with the corresponding ungrouped verts) */ bone_select_array = MEM_mallocN(defbase_tot * sizeof(char), "mask array"); for (i = 0, def = ob->defbase.first; def; def = def->next, i++) { pchan = BKE_pose_channel_find_name(oba->pose, def->name); if (pchan && pchan->bone && (pchan->bone->flag & BONE_SELECTED)) { bone_select_array[i] = TRUE; bone_select_tot++; } else { bone_select_array[i] = FALSE; } } /* if no dverts (i.e. no data for vertex groups exists), we've got an * inconsistent situation, so free hashes and return oirginal mesh */ dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); if (dvert == NULL) { MEM_freeN(bone_select_array); return derivedData; } /* verthash gives mapping from original vertex indices to the new indices (including selected matches only) * key = oldindex, value = newindex */ vertHash = BLI_ghash_int_new("mask vert gh"); /* add vertices which exist in vertexgroups into vertHash for filtering * - dv = for each vertex, what vertexgroups does it belong to * - dw = weight that vertex was assigned to a vertexgroup it belongs to */ for (i = 0, dv = dvert; i < maxVerts; i++, dv++) { MDeformWeight *dw = dv->dw; short found = 0; int j; /* check the groups that vertex is assigned to, and see if it was any use */ for (j = 0; j < dv->totweight; j++, dw++) { if (dw->def_nr < defbase_tot) { if (bone_select_array[dw->def_nr]) { if (dw->weight != 0.0f) { found = TRUE; break; } } } } /* check if include vert in vertHash */ if (mmd->flag & MOD_MASK_INV) { /* if this vert is in the vgroup, don't include it in vertHash */ if (found) continue; } else { /* if this vert isn't in the vgroup, don't include it in vertHash */ if (!found) continue; } /* add to ghash for verts (numVerts acts as counter for mapping) */ BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numVerts)); numVerts++; } /* free temp hashes */ MEM_freeN(bone_select_array); } else { /* --- Using Nominated VertexGroup only --- */ int defgrp_index = defgroup_name_index(ob, mmd->vgroup); /* get dverts */ if (defgrp_index != -1) dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); /* if no vgroup (i.e. dverts) found, return the initial mesh */ if ((defgrp_index == -1) || (dvert == NULL)) return dm; /* hashes for quickly providing a mapping from old to new - use key=oldindex, value=newindex */ vertHash = BLI_ghash_int_new("mask vert2 bh"); /* add vertices which exist in vertexgroup into ghash for filtering */ for (i = 0, dv = dvert; i < maxVerts; i++, dv++) { const int weight_set = defvert_find_weight(dv, defgrp_index) != 0.0f; /* check if include vert in vertHash */ if (mmd->flag & MOD_MASK_INV) { /* if this vert is in the vgroup, don't include it in vertHash */ if (weight_set) continue; } else { /* if this vert isn't in the vgroup, don't include it in vertHash */ if (!weight_set) continue; } /* add to ghash for verts (numVerts acts as counter for mapping) */ BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numVerts)); numVerts++; } } /* hashes for quickly providing a mapping from old to new - use key=oldindex, value=newindex */ edgeHash = BLI_ghash_int_new("mask ed2 gh"); polyHash = BLI_ghash_int_new("mask fa2 gh"); mpoly = dm->getPolyArray(dm); mloop = dm->getLoopArray(dm); loop_mapping = MEM_callocN(sizeof(int) * maxPolys, "mask loopmap"); /* overalloc, assume all polys are seen */ /* loop over edges and faces, and do the same thing to * ensure that they only reference existing verts */ for (i = 0; i < maxEdges; i++) { MEdge me; dm->getEdge(dm, i, &me); /* only add if both verts will be in new mesh */ if (BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me.v1)) && BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me.v2))) { BLI_ghash_insert(edgeHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numEdges)); numEdges++; } } for (i = 0; i < maxPolys; i++) { MPoly *mp = &mpoly[i]; MLoop *ml = mloop + mp->loopstart; int ok = TRUE; int j; for (j = 0; j < mp->totloop; j++, ml++) { if (!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(ml->v))) { ok = FALSE; break; } } /* all verts must be available */ if (ok) { BLI_ghash_insert(polyHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numPolys)); loop_mapping[numPolys] = numLoops; numPolys++; numLoops += mp->totloop; } } /* now we know the number of verts, edges and faces, * we can create the new (reduced) mesh */ result = CDDM_from_template(dm, numVerts, numEdges, 0, numLoops, numPolys); mpoly_new = CDDM_get_polys(result); mloop_new = CDDM_get_loops(result); medge_new = CDDM_get_edges(result); mvert_new = CDDM_get_verts(result); /* using ghash-iterators, map data into new mesh */ /* vertices */ for (hashIter = BLI_ghashIterator_new(vertHash); !BLI_ghashIterator_isDone(hashIter); BLI_ghashIterator_step(hashIter) ) { MVert source; MVert *dest; int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); dm->getVert(dm, oldIndex, &source); dest = &mvert_new[newIndex]; DM_copy_vert_data(dm, result, oldIndex, newIndex, 1); *dest = source; } BLI_ghashIterator_free(hashIter); /* edges */ for (hashIter = BLI_ghashIterator_new(edgeHash); !BLI_ghashIterator_isDone(hashIter); BLI_ghashIterator_step(hashIter)) { MEdge source; MEdge *dest; int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); dm->getEdge(dm, oldIndex, &source); dest = &medge_new[newIndex]; source.v1 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v1))); source.v2 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v2))); DM_copy_edge_data(dm, result, oldIndex, newIndex, 1); *dest = source; } BLI_ghashIterator_free(hashIter); /* faces */ for (hashIter = BLI_ghashIterator_new(polyHash); !BLI_ghashIterator_isDone(hashIter); BLI_ghashIterator_step(hashIter) ) { int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); MPoly *source = &mpoly[oldIndex]; MPoly *dest = &mpoly_new[newIndex]; int oldLoopIndex = source->loopstart; int newLoopIndex = loop_mapping[newIndex]; MLoop *source_loop = &mloop[oldLoopIndex]; MLoop *dest_loop = &mloop_new[newLoopIndex]; DM_copy_poly_data(dm, result, oldIndex, newIndex, 1); DM_copy_loop_data(dm, result, oldLoopIndex, newLoopIndex, source->totloop); *dest = *source; dest->loopstart = newLoopIndex; for (i = 0; i < source->totloop; i++) { dest_loop[i].v = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source_loop[i].v))); dest_loop[i].e = GET_INT_FROM_POINTER(BLI_ghash_lookup(edgeHash, SET_INT_IN_POINTER(source_loop[i].e))); } } BLI_ghashIterator_free(hashIter); MEM_freeN(loop_mapping); /* why is this needed? - campbell */ /* recalculate normals */ CDDM_calc_normals(result); /* free hashes */ BLI_ghash_free(vertHash, NULL, NULL); BLI_ghash_free(edgeHash, NULL, NULL); BLI_ghash_free(polyHash, NULL, NULL); /* return the new mesh */ return result; }
static DerivedMesh *applyModifier(ModifierData *md, Object *UNUSED(ob), DerivedMesh *derivedData, ModifierApplyFlag UNUSED(flag)) { DerivedMesh *dm = derivedData; DerivedMesh *result; BuildModifierData *bmd = (BuildModifierData *) md; int i, j, k; int numFaces_dst, numEdges_dst, numLoops_dst = 0; int *vertMap, *edgeMap, *faceMap; float frac; MPoly *mpoly_dst; MLoop *ml_dst, *ml_src /*, *mloop_dst */; GHashIterator *hashIter; /* maps vert indices in old mesh to indices in new mesh */ GHash *vertHash = BLI_ghash_int_new("build ve apply gh"); /* maps edge indices in new mesh to indices in old mesh */ GHash *edgeHash = BLI_ghash_int_new("build ed apply gh"); GHash *edgeHash2 = BLI_ghash_int_new("build ed apply gh"); const int numVert_src = dm->getNumVerts(dm); const int numEdge_src = dm->getNumEdges(dm); const int numPoly_src = dm->getNumPolys(dm); MPoly *mpoly_src = dm->getPolyArray(dm); MLoop *mloop_src = dm->getLoopArray(dm); MEdge *medge_src = dm->getEdgeArray(dm); MVert *mvert_src = dm->getVertArray(dm); vertMap = MEM_mallocN(sizeof(*vertMap) * numVert_src, "build modifier vertMap"); edgeMap = MEM_mallocN(sizeof(*edgeMap) * numEdge_src, "build modifier edgeMap"); faceMap = MEM_mallocN(sizeof(*faceMap) * numPoly_src, "build modifier faceMap"); #pragma omp parallel sections if (numVert_src + numEdge_src + numPoly_src >= DM_OMP_LIMIT) { #pragma omp section { range_vn_i(vertMap, numVert_src, 0); } #pragma omp section { range_vn_i(edgeMap, numEdge_src, 0); } #pragma omp section { range_vn_i(faceMap, numPoly_src, 0); } } frac = (BKE_scene_frame_get(md->scene) - bmd->start) / bmd->length; CLAMP(frac, 0.0f, 1.0f); numFaces_dst = numPoly_src * frac; numEdges_dst = numEdge_src * frac; /* if there's at least one face, build based on faces */ if (numFaces_dst) { MPoly *mpoly, *mp; MLoop *ml, *mloop; MEdge *medge; if (bmd->randomize) { BLI_array_randomize(faceMap, sizeof(*faceMap), numPoly_src, bmd->seed); } /* get the set of all vert indices that will be in the final mesh, * mapped to the new indices */ mpoly = mpoly_src; mloop = mloop_src; for (i = 0; i < numFaces_dst; i++) { mp = mpoly + faceMap[i]; ml = mloop + mp->loopstart; for (j = 0; j < mp->totloop; j++, ml++) { if (!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(ml->v))) BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(ml->v), SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); } numLoops_dst += mp->totloop; } /* get the set of edges that will be in the new mesh (i.e. all edges * that have both verts in the new mesh) */ medge = medge_src; for (i = 0; i < numEdge_src; i++) { MEdge *me = medge + i; if (BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v1)) && BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v2))) { j = BLI_ghash_size(edgeHash); BLI_ghash_insert(edgeHash, SET_INT_IN_POINTER(j), SET_INT_IN_POINTER(i)); BLI_ghash_insert(edgeHash2, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(j)); } } } else if (numEdges_dst) { MEdge *medge, *me; if (bmd->randomize) BLI_array_randomize(edgeMap, sizeof(*edgeMap), numEdge_src, bmd->seed); /* get the set of all vert indices that will be in the final mesh, * mapped to the new indices */ medge = medge_src; for (i = 0; i < numEdges_dst; i++) { me = medge + edgeMap[i]; if (!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v1))) { BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(me->v1), SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); } if (!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v2))) { BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(me->v2), SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); } } /* get the set of edges that will be in the new mesh */ for (i = 0; i < numEdges_dst; i++) { j = BLI_ghash_size(edgeHash); BLI_ghash_insert(edgeHash, SET_INT_IN_POINTER(j), SET_INT_IN_POINTER(edgeMap[i])); BLI_ghash_insert(edgeHash2, SET_INT_IN_POINTER(edgeMap[i]), SET_INT_IN_POINTER(j)); } } else { int numVerts = numVert_src * frac; if (bmd->randomize) { BLI_array_randomize(vertMap, sizeof(*vertMap), numVert_src, bmd->seed); } /* get the set of all vert indices that will be in the final mesh, * mapped to the new indices */ for (i = 0; i < numVerts; i++) { BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(vertMap[i]), SET_INT_IN_POINTER(i)); } } /* now we know the number of verts, edges and faces, we can create * the mesh */ result = CDDM_from_template(dm, BLI_ghash_size(vertHash), BLI_ghash_size(edgeHash), 0, numLoops_dst, numFaces_dst); /* copy the vertices across */ for (hashIter = BLI_ghashIterator_new(vertHash); BLI_ghashIterator_done(hashIter) == false; BLI_ghashIterator_step(hashIter) ) { MVert source; MVert *dest; int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); source = mvert_src[oldIndex]; dest = CDDM_get_vert(result, newIndex); DM_copy_vert_data(dm, result, oldIndex, newIndex, 1); *dest = source; } BLI_ghashIterator_free(hashIter); /* copy the edges across, remapping indices */ for (i = 0; i < BLI_ghash_size(edgeHash); i++) { MEdge source; MEdge *dest; int oldIndex = GET_INT_FROM_POINTER(BLI_ghash_lookup(edgeHash, SET_INT_IN_POINTER(i))); source = medge_src[oldIndex]; dest = CDDM_get_edge(result, i); source.v1 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v1))); source.v2 = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(source.v2))); DM_copy_edge_data(dm, result, oldIndex, i, 1); *dest = source; } mpoly_dst = CDDM_get_polys(result); /* mloop_dst = */ ml_dst = CDDM_get_loops(result); /* copy the faces across, remapping indices */ k = 0; for (i = 0; i < numFaces_dst; i++) { MPoly *source; MPoly *dest; source = mpoly_src + faceMap[i]; dest = mpoly_dst + i; DM_copy_poly_data(dm, result, faceMap[i], i, 1); *dest = *source; dest->loopstart = k; DM_copy_loop_data(dm, result, source->loopstart, dest->loopstart, dest->totloop); ml_src = mloop_src + source->loopstart; for (j = 0; j < source->totloop; j++, k++, ml_src++, ml_dst++) { ml_dst->v = GET_INT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_INT_IN_POINTER(ml_src->v))); ml_dst->e = GET_INT_FROM_POINTER(BLI_ghash_lookup(edgeHash2, SET_INT_IN_POINTER(ml_src->e))); } } BLI_ghash_free(vertHash, NULL, NULL); BLI_ghash_free(edgeHash, NULL, NULL); BLI_ghash_free(edgeHash2, NULL, NULL); MEM_freeN(vertMap); MEM_freeN(edgeMap); MEM_freeN(faceMap); if (dm->dirty & DM_DIRTY_NORMALS) { result->dirty |= DM_DIRTY_NORMALS; } return result; }
static DerivedMesh * applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, int UNUSED(useRenderParams), int UNUSED(isFinalCalc)) { DerivedMesh *dm = derivedData, *result; ParticleInstanceModifierData *pimd= (ParticleInstanceModifierData*) md; ParticleSimulationData sim; ParticleSystem *psys= NULL; ParticleData *pa= NULL, *pars= NULL; MFace *mface, *orig_mface; MVert *mvert, *orig_mvert; int i,totvert, totpart=0, totface, maxvert, maxface, first_particle=0; short track=ob->trackflag%3, trackneg, axis = pimd->axis; float max_co=0.0, min_co=0.0, temp_co[3], cross[3]; float *size=NULL; DM_ensure_tessface(dm); /* BMESH - UNTIL MODIFIER IS UPDATED FOR MPoly */ trackneg=((ob->trackflag>2)?1:0); if (pimd->ob==ob) { pimd->ob= NULL; return derivedData; } if (pimd->ob) { psys = BLI_findlink(&pimd->ob->particlesystem,pimd->psys-1); if (psys==NULL || psys->totpart==0) return derivedData; } else return derivedData; if (pimd->flag & eParticleInstanceFlag_Parents) totpart+=psys->totpart; if (pimd->flag & eParticleInstanceFlag_Children) { if (totpart==0) first_particle=psys->totpart; totpart+=psys->totchild; } if (totpart==0) return derivedData; sim.scene = md->scene; sim.ob = pimd->ob; sim.psys = psys; sim.psmd = psys_get_modifier(pimd->ob, psys); if (pimd->flag & eParticleInstanceFlag_UseSize) { int p; float *si; si = size = MEM_callocN(totpart * sizeof(float), "particle size array"); if (pimd->flag & eParticleInstanceFlag_Parents) { for (p=0, pa= psys->particles; p<psys->totpart; p++, pa++, si++) *si = pa->size; } if (pimd->flag & eParticleInstanceFlag_Children) { ChildParticle *cpa = psys->child; for (p=0; p<psys->totchild; p++, cpa++, si++) { *si = psys_get_child_size(psys, cpa, 0.0f, NULL); } } } pars=psys->particles; totvert=dm->getNumVerts(dm); totface=dm->getNumTessFaces(dm); maxvert=totvert*totpart; maxface=totface*totpart; psys->lattice=psys_get_lattice(&sim); if (psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) { float min_r[3], max_r[3]; INIT_MINMAX(min_r, max_r); dm->getMinMax(dm, min_r, max_r); min_co=min_r[track]; max_co=max_r[track]; } result = CDDM_from_template(dm, maxvert,dm->getNumEdges(dm)*totpart,maxface, 0, 0); mvert=result->getVertArray(result); orig_mvert=dm->getVertArray(dm); for (i=0; i<maxvert; i++) { MVert *inMV; MVert *mv = mvert + i; ParticleKey state; inMV = orig_mvert + i%totvert; DM_copy_vert_data(dm, result, i%totvert, i, 1); *mv = *inMV; /*change orientation based on object trackflag*/ copy_v3_v3(temp_co, mv->co); mv->co[axis]=temp_co[track]; mv->co[(axis+1)%3]=temp_co[(track+1)%3]; mv->co[(axis+2)%3]=temp_co[(track+2)%3]; if ((psys->flag & (PSYS_HAIR_DONE|PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) && pimd->flag & eParticleInstanceFlag_Path) { float ran = 0.0f; if (pimd->random_position != 0.0f) { BLI_srandom(psys->seed + (i/totvert)%totpart); ran = pimd->random_position * BLI_frand(); } if (pimd->flag & eParticleInstanceFlag_KeepShape) { state.time = pimd->position * (1.0f - ran); } else { state.time=(mv->co[axis]-min_co)/(max_co-min_co) * pimd->position * (1.0f - ran); if (trackneg) state.time=1.0f-state.time; mv->co[axis] = 0.0; } psys_get_particle_on_path(&sim, first_particle + i/totvert, &state,1); normalize_v3(state.vel); /* TODO: incremental rotations somehow */ if (state.vel[axis] < -0.9999f || state.vel[axis] > 0.9999f) { state.rot[0] = 1; state.rot[1] = state.rot[2] = state.rot[3] = 0.0f; } else { float temp[3] = {0.0f,0.0f,0.0f}; temp[axis] = 1.0f; cross_v3_v3v3(cross, temp, state.vel); /* state.vel[axis] is the only component surviving from a dot product with the axis */ axis_angle_to_quat(state.rot,cross,saacos(state.vel[axis])); } } else { state.time=-1.0; psys_get_particle_state(&sim, first_particle + i/totvert, &state,1); } mul_qt_v3(state.rot,mv->co); if (pimd->flag & eParticleInstanceFlag_UseSize) mul_v3_fl(mv->co, size[i/totvert]); add_v3_v3(mv->co, state.co); } mface=result->getTessFaceArray(result); orig_mface=dm->getTessFaceArray(dm); for (i=0; i<maxface; i++) { MFace *inMF; MFace *mf = mface + i; if (pimd->flag & eParticleInstanceFlag_Parents) { if (i/totface>=psys->totpart) { if (psys->part->childtype==PART_CHILD_PARTICLES) { pa=psys->particles+(psys->child+i/totface-psys->totpart)->parent; } else { pa= NULL; } } else { pa=pars+i/totface; } } else { if (psys->part->childtype==PART_CHILD_PARTICLES) { pa=psys->particles+(psys->child+i/totface)->parent; } else { pa= NULL; } } if (pa) { if (pa->alive==PARS_UNBORN && (pimd->flag&eParticleInstanceFlag_Unborn)==0) continue; if (pa->alive==PARS_ALIVE && (pimd->flag&eParticleInstanceFlag_Alive)==0) continue; if (pa->alive==PARS_DEAD && (pimd->flag&eParticleInstanceFlag_Dead)==0) continue; } inMF = orig_mface + i%totface; DM_copy_poly_data(dm, result, i%totface, i, 1); *mf = *inMF; mf->v1+=(i/totface)*totvert; mf->v2+=(i/totface)*totvert; mf->v3+=(i/totface)*totvert; if (mf->v4) { mf->v4+=(i/totface)*totvert; } } CDDM_calc_edges_tessface(result); if (psys->lattice) { end_latt_deform(psys->lattice); psys->lattice= NULL; } if (size) MEM_freeN(size); CDDM_tessfaces_to_faces(result); /*builds ngon faces from tess (mface) faces*/ CDDM_calc_normals(result); return result; }
static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, ModifierApplyFlag UNUSED(flag)) { DerivedMesh *dm = derivedData, *result; ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *) md; ParticleSimulationData sim; ParticleSystem *psys = NULL; ParticleData *pa = NULL; MPoly *mpoly, *orig_mpoly; MLoop *mloop, *orig_mloop; MVert *mvert, *orig_mvert; int totvert, totpoly, totloop /* , totedge */; int maxvert, maxpoly, maxloop, totpart = 0, first_particle = 0; int k, p, p_skip; short track = ob->trackflag % 3, trackneg, axis = pimd->axis; float max_co = 0.0, min_co = 0.0, temp_co[3]; float *size = NULL; trackneg = ((ob->trackflag > 2) ? 1 : 0); if (pimd->ob == ob) { pimd->ob = NULL; return derivedData; } if (pimd->ob) { psys = BLI_findlink(&pimd->ob->particlesystem, pimd->psys - 1); if (psys == NULL || psys->totpart == 0) return derivedData; } else { return derivedData; } if (pimd->flag & eParticleInstanceFlag_Parents) totpart += psys->totpart; if (pimd->flag & eParticleInstanceFlag_Children) { if (totpart == 0) first_particle = psys->totpart; totpart += psys->totchild; } if (totpart == 0) return derivedData; sim.scene = md->scene; sim.ob = pimd->ob; sim.psys = psys; sim.psmd = psys_get_modifier(pimd->ob, psys); if (pimd->flag & eParticleInstanceFlag_UseSize) { float *si; si = size = MEM_callocN(totpart * sizeof(float), "particle size array"); if (pimd->flag & eParticleInstanceFlag_Parents) { for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++, si++) *si = pa->size; } if (pimd->flag & eParticleInstanceFlag_Children) { ChildParticle *cpa = psys->child; for (p = 0; p < psys->totchild; p++, cpa++, si++) { *si = psys_get_child_size(psys, cpa, 0.0f, NULL); } } } totvert = dm->getNumVerts(dm); totpoly = dm->getNumPolys(dm); totloop = dm->getNumLoops(dm); /* totedge = dm->getNumEdges(dm); */ /* UNUSED */ /* count particles */ maxvert = 0; maxpoly = 0; maxloop = 0; for (p = 0; p < totpart; p++) { if (particle_skip(pimd, psys, p)) continue; maxvert += totvert; maxpoly += totpoly; maxloop += totloop; } psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); if (psys->flag & (PSYS_HAIR_DONE | PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) { float min[3], max[3]; INIT_MINMAX(min, max); dm->getMinMax(dm, min, max); min_co = min[track]; max_co = max[track]; } result = CDDM_from_template(dm, maxvert, 0, 0, maxloop, maxpoly); mvert = result->getVertArray(result); orig_mvert = dm->getVertArray(dm); mpoly = result->getPolyArray(result); orig_mpoly = dm->getPolyArray(dm); mloop = result->getLoopArray(result); orig_mloop = dm->getLoopArray(dm); for (p = 0, p_skip = 0; p < totpart; p++) { float prev_dir[3]; float frame[4]; /* frame orientation quaternion */ /* skip particle? */ if (particle_skip(pimd, psys, p)) continue; /* set vertices coordinates */ for (k = 0; k < totvert; k++) { ParticleKey state; MVert *inMV; MVert *mv = mvert + p_skip * totvert + k; inMV = orig_mvert + k; DM_copy_vert_data(dm, result, k, p_skip * totvert + k, 1); *mv = *inMV; /*change orientation based on object trackflag*/ copy_v3_v3(temp_co, mv->co); mv->co[axis] = temp_co[track]; mv->co[(axis + 1) % 3] = temp_co[(track + 1) % 3]; mv->co[(axis + 2) % 3] = temp_co[(track + 2) % 3]; /* get particle state */ if ((psys->flag & (PSYS_HAIR_DONE | PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) && (pimd->flag & eParticleInstanceFlag_Path)) { float ran = 0.0f; if (pimd->random_position != 0.0f) { ran = pimd->random_position * BLI_hash_frand(psys->seed + p); } if (pimd->flag & eParticleInstanceFlag_KeepShape) { state.time = pimd->position * (1.0f - ran); } else { state.time = (mv->co[axis] - min_co) / (max_co - min_co) * pimd->position * (1.0f - ran); if (trackneg) state.time = 1.0f - state.time; mv->co[axis] = 0.0; } psys_get_particle_on_path(&sim, first_particle + p, &state, 1); normalize_v3(state.vel); /* Incrementally Rotating Frame (Bishop Frame) */ if (k == 0) { float hairmat[4][4]; float mat[3][3]; if (first_particle + p < psys->totpart) pa = psys->particles + first_particle + p; else { ChildParticle *cpa = psys->child + (p - psys->totpart); pa = psys->particles + cpa->parent; } psys_mat_hair_to_global(sim.ob, sim.psmd->dm, sim.psys->part->from, pa, hairmat); copy_m3_m4(mat, hairmat); /* to quaternion */ mat3_to_quat(frame, mat); /* note: direction is same as normal vector currently, * but best to keep this separate so the frame can be * rotated later if necessary */ copy_v3_v3(prev_dir, state.vel); } else { float rot[4]; /* incrementally rotate along bend direction */ rotation_between_vecs_to_quat(rot, prev_dir, state.vel); mul_qt_qtqt(frame, rot, frame); copy_v3_v3(prev_dir, state.vel); } copy_qt_qt(state.rot, frame); #if 0 /* Absolute Frame (Frenet Frame) */ if (state.vel[axis] < -0.9999f || state.vel[axis] > 0.9999f) { unit_qt(state.rot); } else { float cross[3]; float temp[3] = {0.0f, 0.0f, 0.0f}; temp[axis] = 1.0f; cross_v3_v3v3(cross, temp, state.vel); /* state.vel[axis] is the only component surviving from a dot product with the axis */ axis_angle_to_quat(state.rot, cross, saacos(state.vel[axis])); } #endif } else { state.time = -1.0; psys_get_particle_state(&sim, first_particle + p, &state, 1); } mul_qt_v3(state.rot, mv->co); if (pimd->flag & eParticleInstanceFlag_UseSize) mul_v3_fl(mv->co, size[p]); add_v3_v3(mv->co, state.co); } /* create polys and loops */ for (k = 0; k < totpoly; k++) { MPoly *inMP = orig_mpoly + k; MPoly *mp = mpoly + p_skip * totpoly + k; DM_copy_poly_data(dm, result, k, p_skip * totpoly + k, 1); *mp = *inMP; mp->loopstart += p_skip * totloop; { MLoop *inML = orig_mloop + inMP->loopstart; MLoop *ml = mloop + mp->loopstart; int j = mp->totloop; DM_copy_loop_data(dm, result, inMP->loopstart, mp->loopstart, j); for (; j; j--, ml++, inML++) { ml->v = inML->v + (p_skip * totvert); } } } p_skip++; } CDDM_calc_edges(result); if (psys->lattice_deform_data) { end_latt_deform(psys->lattice_deform_data); psys->lattice_deform_data = NULL; } if (size) MEM_freeN(size); result->dirty |= DM_DIRTY_NORMALS; return result; }
static DerivedMesh *doMirrorOnAxis(MirrorModifierData *mmd, Object *ob, DerivedMesh *dm, int axis) { const float tolerance_sq = mmd->tolerance * mmd->tolerance; const int do_vtargetmap = !(mmd->flag & MOD_MIR_NO_MERGE); int is_vtargetmap = FALSE; /* true when it should be used */ DerivedMesh *result; const int maxVerts = dm->getNumVerts(dm); const int maxEdges = dm->getNumEdges(dm); const int maxLoops = dm->getNumLoops(dm); const int maxPolys = dm->getNumPolys(dm); MVert *mv, *mv_prev; MEdge *me; MLoop *ml; MPoly *mp; float mtx[4][4]; int i, j; int a, totshape; int *vtargetmap = NULL, *vtmap_a = NULL, *vtmap_b = NULL; /* mtx is the mirror transformation */ unit_m4(mtx); mtx[axis][axis] = -1.0f; if (mmd->mirror_ob) { float tmp[4][4]; float itmp[4][4]; /* tmp is a transform from coords relative to the object's own origin, * to coords relative to the mirror object origin */ invert_m4_m4(tmp, mmd->mirror_ob->obmat); mult_m4_m4m4(tmp, tmp, ob->obmat); /* itmp is the reverse transform back to origin-relative coordinates */ invert_m4_m4(itmp, tmp); /* combine matrices to get a single matrix that translates coordinates into * mirror-object-relative space, does the mirror, and translates back to * origin-relative space */ mult_m4_m4m4(mtx, mtx, tmp); mult_m4_m4m4(mtx, itmp, mtx); } result = CDDM_from_template(dm, maxVerts * 2, maxEdges * 2, 0, maxLoops * 2, maxPolys * 2); /*copy customdata to original geometry*/ DM_copy_vert_data(dm, result, 0, 0, maxVerts); DM_copy_edge_data(dm, result, 0, 0, maxEdges); DM_copy_loop_data(dm, result, 0, 0, maxLoops); DM_copy_poly_data(dm, result, 0, 0, maxPolys); /* subsurf for eg wont have mesh data in the */ /* now add mvert/medge/mface layers */ if (!CustomData_has_layer(&dm->vertData, CD_MVERT)) { dm->copyVertArray(dm, CDDM_get_verts(result)); } if (!CustomData_has_layer(&dm->edgeData, CD_MEDGE)) { dm->copyEdgeArray(dm, CDDM_get_edges(result)); } if (!CustomData_has_layer(&dm->polyData, CD_MPOLY)) { dm->copyLoopArray(dm, CDDM_get_loops(result)); dm->copyPolyArray(dm, CDDM_get_polys(result)); } /* copy customdata to new geometry, * copy from its self because this data may have been created in the checks above */ DM_copy_vert_data(result, result, 0, maxVerts, maxVerts); DM_copy_edge_data(result, result, 0, maxEdges, maxEdges); /* loops are copied later */ DM_copy_poly_data(result, result, 0, maxPolys, maxPolys); if (do_vtargetmap) { /* second half is filled with -1 */ vtargetmap = MEM_mallocN(sizeof(int) * maxVerts * 2, "MOD_mirror tarmap"); vtmap_a = vtargetmap; vtmap_b = vtargetmap + maxVerts; } /* mirror vertex coordinates */ mv_prev = CDDM_get_verts(result); mv = mv_prev + maxVerts; for (i = 0; i < maxVerts; i++, mv++, mv_prev++) { mul_m4_v3(mtx, mv->co); if (do_vtargetmap) { /* compare location of the original and mirrored vertex, to see if they * should be mapped for merging */ if (UNLIKELY(len_squared_v3v3(mv_prev->co, mv->co) < tolerance_sq)) { *vtmap_a = maxVerts + i; is_vtargetmap = TRUE; } else { *vtmap_a = -1; } *vtmap_b = -1; /* fill here to avoid 2x loops */ vtmap_a++; vtmap_b++; } } /* handle shape keys */ totshape = CustomData_number_of_layers(&result->vertData, CD_SHAPEKEY); for (a = 0; a < totshape; a++) { float (*cos)[3] = CustomData_get_layer_n(&result->vertData, CD_SHAPEKEY, a); for (i = maxVerts; i < result->numVertData; i++) { mul_m4_v3(mtx, cos[i]); } } /* adjust mirrored edge vertex indices */ me = CDDM_get_edges(result) + maxEdges; for (i = 0; i < maxEdges; i++, me++) { me->v1 += maxVerts; me->v2 += maxVerts; } /* adjust mirrored poly loopstart indices, and reverse loop order (normals) */ mp = CDDM_get_polys(result) + maxPolys; ml = CDDM_get_loops(result); for (i = 0; i < maxPolys; i++, mp++) { MLoop *ml2; int e; /* reverse the loop, but we keep the first vertex in the face the same, * to ensure that quads are split the same way as on the other side */ DM_copy_loop_data(result, result, mp->loopstart, mp->loopstart + maxLoops, 1); for (j = 1; j < mp->totloop; j++) DM_copy_loop_data(result, result, mp->loopstart + j, mp->loopstart + maxLoops + mp->totloop - j, 1); ml2 = ml + mp->loopstart + maxLoops; e = ml2[0].e; for (j = 0; j < mp->totloop - 1; j++) { ml2[j].e = ml2[j + 1].e; } ml2[mp->totloop - 1].e = e; mp->loopstart += maxLoops; } /* adjust mirrored loop vertex and edge indices */ ml = CDDM_get_loops(result) + maxLoops; for (i = 0; i < maxLoops; i++, ml++) { ml->v += maxVerts; ml->e += maxEdges; } /* handle uvs, * let tessface recalc handle updating the MTFace data */ if (mmd->flag & (MOD_MIR_MIRROR_U | MOD_MIR_MIRROR_V)) { const int do_mirr_u = (mmd->flag & MOD_MIR_MIRROR_U) != 0; const int do_mirr_v = (mmd->flag & MOD_MIR_MIRROR_V) != 0; const int totuv = CustomData_number_of_layers(&result->loopData, CD_MLOOPUV); for (a = 0; a < totuv; a++) { MLoopUV *dmloopuv = CustomData_get_layer_n(&result->loopData, CD_MLOOPUV, a); int j = maxLoops; dmloopuv += j; /* second set of loops only */ for (; j-- > 0; dmloopuv++) { if (do_mirr_u) dmloopuv->uv[0] = 1.0f - dmloopuv->uv[0]; if (do_mirr_v) dmloopuv->uv[1] = 1.0f - dmloopuv->uv[1]; } } } /* handle vgroup stuff */ if ((mmd->flag & MOD_MIR_VGROUP) && CustomData_has_layer(&result->vertData, CD_MDEFORMVERT)) { MDeformVert *dvert = (MDeformVert *) CustomData_get_layer(&result->vertData, CD_MDEFORMVERT) + maxVerts; int *flip_map = NULL, flip_map_len = 0; flip_map = defgroup_flip_map(ob, &flip_map_len, FALSE); if (flip_map) { for (i = 0; i < maxVerts; dvert++, i++) { /* merged vertices get both groups, others get flipped */ if (do_vtargetmap && (vtargetmap[i] != -1)) defvert_flip_merged(dvert, flip_map, flip_map_len); else defvert_flip(dvert, flip_map, flip_map_len); } MEM_freeN(flip_map); } } if (do_vtargetmap) { /* slow - so only call if one or more merge verts are found, * users may leave this on and not realize there is nothing to merge - campbell */ if (is_vtargetmap) { result = CDDM_merge_verts(result, vtargetmap); } MEM_freeN(vtargetmap); } return result; }
static DerivedMesh *arrayModifier_doArray( ArrayModifierData *amd, Scene *scene, Object *ob, DerivedMesh *dm, ModifierApplyFlag flag) { const float eps = 1e-6f; const MVert *src_mvert; MVert *mv, *mv_prev, *result_dm_verts; MEdge *me; MLoop *ml; MPoly *mp; int i, j, c, count; float length = amd->length; /* offset matrix */ float offset[4][4]; float scale[3]; bool offset_has_scale; float current_offset[4][4]; float final_offset[4][4]; int *full_doubles_map = NULL; int tot_doubles; const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0; const bool use_recalc_normals = (dm->dirty & DM_DIRTY_NORMALS) || use_merge; const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob); /* allow pole vertices to be used by many faces */ const bool with_follow = use_offset_ob; int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_npolys = 0, start_cap_nloops = 0; int end_cap_nverts = 0, end_cap_nedges = 0, end_cap_npolys = 0, end_cap_nloops = 0; int result_nverts = 0, result_nedges = 0, result_npolys = 0, result_nloops = 0; int chunk_nverts, chunk_nedges, chunk_nloops, chunk_npolys; int first_chunk_start, first_chunk_nverts, last_chunk_start, last_chunk_nverts; DerivedMesh *result, *start_cap_dm = NULL, *end_cap_dm = NULL; chunk_nverts = dm->getNumVerts(dm); chunk_nedges = dm->getNumEdges(dm); chunk_nloops = dm->getNumLoops(dm); chunk_npolys = dm->getNumPolys(dm); count = amd->count; if (amd->start_cap && amd->start_cap != ob && amd->start_cap->type == OB_MESH) { start_cap_dm = get_dm_for_modifier(amd->start_cap, flag); if (start_cap_dm) { start_cap_nverts = start_cap_dm->getNumVerts(start_cap_dm); start_cap_nedges = start_cap_dm->getNumEdges(start_cap_dm); start_cap_nloops = start_cap_dm->getNumLoops(start_cap_dm); start_cap_npolys = start_cap_dm->getNumPolys(start_cap_dm); } } if (amd->end_cap && amd->end_cap != ob && amd->end_cap->type == OB_MESH) { end_cap_dm = get_dm_for_modifier(amd->end_cap, flag); if (end_cap_dm) { end_cap_nverts = end_cap_dm->getNumVerts(end_cap_dm); end_cap_nedges = end_cap_dm->getNumEdges(end_cap_dm); end_cap_nloops = end_cap_dm->getNumLoops(end_cap_dm); end_cap_npolys = end_cap_dm->getNumPolys(end_cap_dm); } } /* Build up offset array, cumulating all settings options */ unit_m4(offset); src_mvert = dm->getVertArray(dm); if (amd->offset_type & MOD_ARR_OFF_CONST) add_v3_v3v3(offset[3], offset[3], amd->offset); if (amd->offset_type & MOD_ARR_OFF_RELATIVE) { for (j = 0; j < 3; j++) offset[3][j] += amd->scale[j] * vertarray_size(src_mvert, chunk_nverts, j); } if (use_offset_ob) { float obinv[4][4]; float result_mat[4][4]; if (ob) invert_m4_m4(obinv, ob->obmat); else unit_m4(obinv); mul_m4_series(result_mat, offset, obinv, amd->offset_ob->obmat); copy_m4_m4(offset, result_mat); } /* Check if there is some scaling. If scaling, then we will not translate mapping */ mat4_to_size(scale, offset); offset_has_scale = !is_one_v3(scale); if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob) { Curve *cu = amd->curve_ob->data; if (cu) { #ifdef CYCLIC_DEPENDENCY_WORKAROUND if (amd->curve_ob->curve_cache == NULL) { BKE_displist_make_curveTypes(scene, amd->curve_ob, false); } #endif if (amd->curve_ob->curve_cache && amd->curve_ob->curve_cache->path) { float scale = mat4_to_scale(amd->curve_ob->obmat); length = scale * amd->curve_ob->curve_cache->path->totdist; } } } /* calculate the maximum number of copies which will fit within the * prescribed length */ if (amd->fit_type == MOD_ARR_FITLENGTH || amd->fit_type == MOD_ARR_FITCURVE) { float dist = len_v3(offset[3]); if (dist > eps) { /* this gives length = first copy start to last copy end * add a tiny offset for floating point rounding errors */ count = (length + eps) / dist; } else { /* if the offset has no translation, just make one copy */ count = 1; } } if (count < 1) count = 1; /* The number of verts, edges, loops, polys, before eventually merging doubles */ result_nverts = chunk_nverts * count + start_cap_nverts + end_cap_nverts; result_nedges = chunk_nedges * count + start_cap_nedges + end_cap_nedges; result_nloops = chunk_nloops * count + start_cap_nloops + end_cap_nloops; result_npolys = chunk_npolys * count + start_cap_npolys + end_cap_npolys; /* Initialize a result dm */ result = CDDM_from_template(dm, result_nverts, result_nedges, 0, result_nloops, result_npolys); result_dm_verts = CDDM_get_verts(result); if (use_merge) { /* Will need full_doubles_map for handling merge */ full_doubles_map = MEM_mallocN(sizeof(int) * result_nverts, "mod array doubles map"); fill_vn_i(full_doubles_map, result_nverts, -1); } /* copy customdata to original geometry */ DM_copy_vert_data(dm, result, 0, 0, chunk_nverts); DM_copy_edge_data(dm, result, 0, 0, chunk_nedges); DM_copy_loop_data(dm, result, 0, 0, chunk_nloops); DM_copy_poly_data(dm, result, 0, 0, chunk_npolys); /* subsurf for eg wont have mesh data in the * now add mvert/medge/mface layers */ if (!CustomData_has_layer(&dm->vertData, CD_MVERT)) { dm->copyVertArray(dm, result_dm_verts); } if (!CustomData_has_layer(&dm->edgeData, CD_MEDGE)) { dm->copyEdgeArray(dm, CDDM_get_edges(result)); } if (!CustomData_has_layer(&dm->polyData, CD_MPOLY)) { dm->copyLoopArray(dm, CDDM_get_loops(result)); dm->copyPolyArray(dm, CDDM_get_polys(result)); } /* Remember first chunk, in case of cap merge */ first_chunk_start = 0; first_chunk_nverts = chunk_nverts; unit_m4(current_offset); for (c = 1; c < count; c++) { /* copy customdata to new geometry */ DM_copy_vert_data(result, result, 0, c * chunk_nverts, chunk_nverts); DM_copy_edge_data(result, result, 0, c * chunk_nedges, chunk_nedges); DM_copy_loop_data(result, result, 0, c * chunk_nloops, chunk_nloops); DM_copy_poly_data(result, result, 0, c * chunk_npolys, chunk_npolys); mv_prev = result_dm_verts; mv = mv_prev + c * chunk_nverts; /* recalculate cumulative offset here */ mul_m4_m4m4(current_offset, current_offset, offset); /* apply offset to all new verts */ for (i = 0; i < chunk_nverts; i++, mv++, mv_prev++) { mul_m4_v3(current_offset, mv->co); /* We have to correct normals too, if we do not tag them as dirty! */ if (!use_recalc_normals) { float no[3]; normal_short_to_float_v3(no, mv->no); mul_mat3_m4_v3(current_offset, no); normalize_v3(no); normal_float_to_short_v3(mv->no, no); } } /* adjust edge vertex indices */ me = CDDM_get_edges(result) + c * chunk_nedges; for (i = 0; i < chunk_nedges; i++, me++) { me->v1 += c * chunk_nverts; me->v2 += c * chunk_nverts; } mp = CDDM_get_polys(result) + c * chunk_npolys; for (i = 0; i < chunk_npolys; i++, mp++) { mp->loopstart += c * chunk_nloops; } /* adjust loop vertex and edge indices */ ml = CDDM_get_loops(result) + c * chunk_nloops; for (i = 0; i < chunk_nloops; i++, ml++) { ml->v += c * chunk_nverts; ml->e += c * chunk_nedges; } /* Handle merge between chunk n and n-1 */ if (use_merge && (c >= 1)) { if (!offset_has_scale && (c >= 2)) { /* Mapping chunk 3 to chunk 2 is a translation of mapping 2 to 1 * ... that is except if scaling makes the distance grow */ int k; int this_chunk_index = c * chunk_nverts; int prev_chunk_index = (c - 1) * chunk_nverts; for (k = 0; k < chunk_nverts; k++, this_chunk_index++, prev_chunk_index++) { int target = full_doubles_map[prev_chunk_index]; if (target != -1) { target += chunk_nverts; /* translate mapping */ if (full_doubles_map[target] != -1) { if (with_follow) { target = full_doubles_map[target]; } else { /* The rule here is to not follow mapping to chunk N-2, which could be too far * so if target vertex was itself mapped, then this vertex is not mapped */ target = -1; } } } full_doubles_map[this_chunk_index] = target; } } else { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, (c - 1) * chunk_nverts, chunk_nverts, c * chunk_nverts, chunk_nverts, amd->merge_dist, with_follow); } } } last_chunk_start = (count - 1) * chunk_nverts; last_chunk_nverts = chunk_nverts; copy_m4_m4(final_offset, current_offset); if (use_merge && (amd->flags & MOD_ARR_MERGEFINAL) && (count > 1)) { /* Merge first and last copies */ dm_mvert_map_doubles( full_doubles_map, result_dm_verts, last_chunk_start, last_chunk_nverts, first_chunk_start, first_chunk_nverts, amd->merge_dist, with_follow); } /* start capping */ if (start_cap_dm) { float start_offset[4][4]; int start_cap_start = result_nverts - start_cap_nverts - end_cap_nverts; invert_m4_m4(start_offset, offset); dm_merge_transform( result, start_cap_dm, start_offset, result_nverts - start_cap_nverts - end_cap_nverts, result_nedges - start_cap_nedges - end_cap_nedges, result_nloops - start_cap_nloops - end_cap_nloops, result_npolys - start_cap_npolys - end_cap_npolys, start_cap_nverts, start_cap_nedges, start_cap_nloops, start_cap_npolys); /* Identify doubles with first chunk */ if (use_merge) { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, first_chunk_start, first_chunk_nverts, start_cap_start, start_cap_nverts, amd->merge_dist, false); } } if (end_cap_dm) { float end_offset[4][4]; int end_cap_start = result_nverts - end_cap_nverts; mul_m4_m4m4(end_offset, current_offset, offset); dm_merge_transform( result, end_cap_dm, end_offset, result_nverts - end_cap_nverts, result_nedges - end_cap_nedges, result_nloops - end_cap_nloops, result_npolys - end_cap_npolys, end_cap_nverts, end_cap_nedges, end_cap_nloops, end_cap_npolys); /* Identify doubles with last chunk */ if (use_merge) { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, last_chunk_start, last_chunk_nverts, end_cap_start, end_cap_nverts, amd->merge_dist, false); } } /* done capping */ /* Handle merging */ tot_doubles = 0; if (use_merge) { for (i = 0; i < result_nverts; i++) { if (full_doubles_map[i] != -1) { if (i == full_doubles_map[i]) { full_doubles_map[i] = -1; } else { tot_doubles++; } } } if (tot_doubles > 0) { result = CDDM_merge_verts(result, full_doubles_map, tot_doubles, CDDM_MERGE_VERTS_DUMP_IF_EQUAL); } MEM_freeN(full_doubles_map); } /* In case org dm has dirty normals, or we made some merging, mark normals as dirty in new dm! * TODO: we may need to set other dirty flags as well? */ if (use_recalc_normals) { result->dirty |= DM_DIRTY_NORMALS; } return result; }
static void dm_merge_transform( DerivedMesh *result, DerivedMesh *cap_dm, float cap_offset[4][4], unsigned int cap_verts_index, unsigned int cap_edges_index, int cap_loops_index, int cap_polys_index, int cap_nverts, int cap_nedges, int cap_nloops, int cap_npolys) { int *index_orig; int i; MVert *mv; MEdge *me; MLoop *ml; MPoly *mp; /* needed for subsurf so arrays are allocated */ cap_dm->getVertArray(cap_dm); cap_dm->getEdgeArray(cap_dm); cap_dm->getLoopArray(cap_dm); cap_dm->getPolyArray(cap_dm); DM_copy_vert_data(cap_dm, result, 0, cap_verts_index, cap_nverts); DM_copy_edge_data(cap_dm, result, 0, cap_edges_index, cap_nedges); DM_copy_loop_data(cap_dm, result, 0, cap_loops_index, cap_nloops); DM_copy_poly_data(cap_dm, result, 0, cap_polys_index, cap_npolys); mv = CDDM_get_verts(result) + cap_verts_index; for (i = 0; i < cap_nverts; i++, mv++) { mul_m4_v3(cap_offset, mv->co); /* Reset MVert flags for caps */ mv->flag = mv->bweight = 0; } /* adjust cap edge vertex indices */ me = CDDM_get_edges(result) + cap_edges_index; for (i = 0; i < cap_nedges; i++, me++) { me->v1 += cap_verts_index; me->v2 += cap_verts_index; } /* adjust cap poly loopstart indices */ mp = CDDM_get_polys(result) + cap_polys_index; for (i = 0; i < cap_npolys; i++, mp++) { mp->loopstart += cap_loops_index; } /* adjust cap loop vertex and edge indices */ ml = CDDM_get_loops(result) + cap_loops_index; for (i = 0; i < cap_nloops; i++, ml++) { ml->v += cap_verts_index; ml->e += cap_edges_index; } /* set origindex */ index_orig = result->getVertDataArray(result, CD_ORIGINDEX); if (index_orig) { fill_vn_i(index_orig + cap_verts_index, cap_nverts, ORIGINDEX_NONE); } index_orig = result->getEdgeDataArray(result, CD_ORIGINDEX); if (index_orig) { fill_vn_i(index_orig + cap_edges_index, cap_nedges, ORIGINDEX_NONE); } index_orig = result->getPolyDataArray(result, CD_ORIGINDEX); if (index_orig) { fill_vn_i(index_orig + cap_polys_index, cap_npolys, ORIGINDEX_NONE); } index_orig = result->getLoopDataArray(result, CD_ORIGINDEX); if (index_orig) { fill_vn_i(index_orig + cap_loops_index, cap_nloops, ORIGINDEX_NONE); } }
static DerivedMesh *applyModifier( ModifierData *md, Object *ob, DerivedMesh *dm, ModifierApplyFlag UNUSED(flag)) { DerivedMesh *result; const SolidifyModifierData *smd = (SolidifyModifierData *) md; MVert *mv, *mvert, *orig_mvert; MEdge *ed, *medge, *orig_medge; MLoop *ml, *mloop, *orig_mloop; MPoly *mp, *mpoly, *orig_mpoly; const unsigned int numVerts = (unsigned int)dm->getNumVerts(dm); const unsigned int numEdges = (unsigned int)dm->getNumEdges(dm); const unsigned int numFaces = (unsigned int)dm->getNumPolys(dm); const unsigned int numLoops = (unsigned int)dm->getNumLoops(dm); unsigned int newLoops = 0, newFaces = 0, newEdges = 0, newVerts = 0, rimVerts = 0; /* only use material offsets if we have 2 or more materials */ const short mat_nr_max = ob->totcol > 1 ? ob->totcol - 1 : 0; const short mat_ofs = mat_nr_max ? smd->mat_ofs : 0; const short mat_ofs_rim = mat_nr_max ? smd->mat_ofs_rim : 0; /* use for edges */ /* over-alloc new_vert_arr, old_vert_arr */ unsigned int *new_vert_arr = NULL; STACK_DECLARE(new_vert_arr); unsigned int *new_edge_arr = NULL; STACK_DECLARE(new_edge_arr); unsigned int *old_vert_arr = MEM_callocN(sizeof(*old_vert_arr) * (size_t)numVerts, "old_vert_arr in solidify"); unsigned int *edge_users = NULL; char *edge_order = NULL; float (*vert_nors)[3] = NULL; float (*face_nors)[3] = NULL; const bool need_face_normals = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) || (smd->flag & MOD_SOLIDIFY_EVEN); const float ofs_orig = -(((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset); const float ofs_new = smd->offset + ofs_orig; const float offset_fac_vg = smd->offset_fac_vg; const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0; const bool do_clamp = (smd->offset_clamp != 0.0f); const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) == 0; /* weights */ MDeformVert *dvert; const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0; int defgrp_index; /* array size is doubled in case of using a shell */ const unsigned int stride = do_shell ? 2 : 1; modifier_get_vgroup(ob, dm, smd->defgrp_name, &dvert, &defgrp_index); orig_mvert = dm->getVertArray(dm); orig_medge = dm->getEdgeArray(dm); orig_mloop = dm->getLoopArray(dm); orig_mpoly = dm->getPolyArray(dm); if (need_face_normals) { /* calculate only face normals */ face_nors = MEM_mallocN(sizeof(*face_nors) * (size_t)numFaces, __func__); BKE_mesh_calc_normals_poly( orig_mvert, NULL, (int)numVerts, orig_mloop, orig_mpoly, (int)numLoops, (int)numFaces, face_nors, true); } STACK_INIT(new_vert_arr, numVerts * 2); STACK_INIT(new_edge_arr, numEdges * 2); if (smd->flag & MOD_SOLIDIFY_RIM) { BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(numVerts, __func__); unsigned int eidx; unsigned int i; #define INVALID_UNUSED ((unsigned int)-1) #define INVALID_PAIR ((unsigned int)-2) new_vert_arr = MEM_mallocN(sizeof(*new_vert_arr) * (size_t)(numVerts * 2), __func__); new_edge_arr = MEM_mallocN(sizeof(*new_edge_arr) * (size_t)((numEdges * 2) + numVerts), __func__); edge_users = MEM_mallocN(sizeof(*edge_users) * (size_t)numEdges, "solid_mod edges"); edge_order = MEM_mallocN(sizeof(*edge_order) * (size_t)numEdges, "solid_mod eorder"); /* save doing 2 loops here... */ #if 0 copy_vn_i(edge_users, numEdges, INVALID_UNUSED); #endif for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { edge_users[eidx] = INVALID_UNUSED; } for (i = 0, mp = orig_mpoly; i < numFaces; i++, mp++) { MLoop *ml_prev; int j; ml = orig_mloop + mp->loopstart; ml_prev = ml + (mp->totloop - 1); for (j = 0; j < mp->totloop; j++, ml++) { /* add edge user */ eidx = ml_prev->e; if (edge_users[eidx] == INVALID_UNUSED) { ed = orig_medge + eidx; BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) && ELEM(ml->v, ed->v1, ed->v2)); edge_users[eidx] = (ml_prev->v > ml->v) == (ed->v1 < ed->v2) ? i : (i + numFaces); edge_order[eidx] = j; } else { edge_users[eidx] = INVALID_PAIR; } ml_prev = ml; } } for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) { BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v1); BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v2); STACK_PUSH(new_edge_arr, eidx); newFaces++; newLoops += 4; } } for (i = 0; i < numVerts; i++) { if (BLI_BITMAP_TEST(orig_mvert_tag, i)) { old_vert_arr[i] = STACK_SIZE(new_vert_arr); STACK_PUSH(new_vert_arr, i); rimVerts++; } else { old_vert_arr[i] = INVALID_UNUSED; } } MEM_freeN(orig_mvert_tag); } if (do_shell == false) { /* only add rim vertices */ newVerts = rimVerts; /* each extruded face needs an opposite edge */ newEdges = newFaces; } else { /* (stride == 2) in this case, so no need to add newVerts/newEdges */ BLI_assert(newVerts == 0); BLI_assert(newEdges == 0); } if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) { vert_nors = MEM_callocN(sizeof(float) * (size_t)numVerts * 3, "mod_solid_vno_hq"); dm_calc_normal(dm, face_nors, vert_nors); } result = CDDM_from_template(dm, (int)((numVerts * stride) + newVerts), (int)((numEdges * stride) + newEdges + rimVerts), 0, (int)((numLoops * stride) + newLoops), (int)((numFaces * stride) + newFaces)); mpoly = CDDM_get_polys(result); mloop = CDDM_get_loops(result); medge = CDDM_get_edges(result); mvert = CDDM_get_verts(result); if (do_shell) { DM_copy_vert_data(dm, result, 0, 0, (int)numVerts); DM_copy_vert_data(dm, result, 0, (int)numVerts, (int)numVerts); DM_copy_edge_data(dm, result, 0, 0, (int)numEdges); DM_copy_edge_data(dm, result, 0, (int)numEdges, (int)numEdges); DM_copy_loop_data(dm, result, 0, 0, (int)numLoops); DM_copy_loop_data(dm, result, 0, (int)numLoops, (int)numLoops); DM_copy_poly_data(dm, result, 0, 0, (int)numFaces); DM_copy_poly_data(dm, result, 0, (int)numFaces, (int)numFaces); } else { int i, j; DM_copy_vert_data(dm, result, 0, 0, (int)numVerts); for (i = 0, j = (int)numVerts; i < numVerts; i++) { if (old_vert_arr[i] != INVALID_UNUSED) { DM_copy_vert_data(dm, result, i, j, 1); j++; } } DM_copy_edge_data(dm, result, 0, 0, (int)numEdges); for (i = 0, j = (int)numEdges; i < numEdges; i++) { if (!ELEM(edge_users[i], INVALID_UNUSED, INVALID_PAIR)) { MEdge *ed_src, *ed_dst; DM_copy_edge_data(dm, result, i, j, 1); ed_src = &medge[i]; ed_dst = &medge[j]; ed_dst->v1 = old_vert_arr[ed_src->v1] + numVerts; ed_dst->v2 = old_vert_arr[ed_src->v2] + numVerts; j++; } } /* will be created later */ DM_copy_loop_data(dm, result, 0, 0, (int)numLoops); DM_copy_poly_data(dm, result, 0, 0, (int)numFaces); } #undef INVALID_UNUSED #undef INVALID_PAIR /* initializes: (i_end, do_shell_align, mv) */ #define INIT_VERT_ARRAY_OFFSETS(test) \ if (((ofs_new >= ofs_orig) == do_flip) == test) { \ i_end = numVerts; \ do_shell_align = true; \ mv = mvert; \ } \ else { \ if (do_shell) { \ i_end = numVerts; \ do_shell_align = true; \ } \ else { \ i_end = newVerts ; \ do_shell_align = false; \ } \ mv = &mvert[numVerts]; \ } (void)0 /* flip normals */ if (do_shell) { unsigned int i; mp = mpoly + numFaces; for (i = 0; i < dm->numPolyData; i++, mp++) { MLoop *ml2; unsigned int e; int j; /* reverses the loop direction (MLoop.v as well as custom-data) * MLoop.e also needs to be corrected too, done in a separate loop below. */ ml2 = mloop + mp->loopstart + dm->numLoopData; for (j = 0; j < mp->totloop; j++) { CustomData_copy_data(&dm->loopData, &result->loopData, mp->loopstart + j, mp->loopstart + (mp->totloop - j - 1) + dm->numLoopData, 1); } if (mat_ofs) { mp->mat_nr += mat_ofs; CLAMP(mp->mat_nr, 0, mat_nr_max); } e = ml2[0].e; for (j = 0; j < mp->totloop - 1; j++) { ml2[j].e = ml2[j + 1].e; } ml2[mp->totloop - 1].e = e; mp->loopstart += dm->numLoopData; for (j = 0; j < mp->totloop; j++) { ml2[j].e += numEdges; ml2[j].v += numVerts; } } for (i = 0, ed = medge + numEdges; i < numEdges; i++, ed++) { ed->v1 += numVerts; ed->v2 += numVerts; } } /* note, copied vertex layers don't 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; /* for clamping */ float *vert_lens = NULL; const float offset = fabsf(smd->offset) * smd->offset_clamp; const float offset_sq = offset * offset; if (do_clamp) { unsigned int i; vert_lens = MEM_mallocN(sizeof(float) * numVerts, "vert_lens"); copy_vn_fl(vert_lens, (int)numVerts, FLT_MAX); for (i = 0; i < numEdges; i++) { const float ed_len_sq = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); vert_lens[medge[i].v1] = min_ff(vert_lens[medge[i].v1], ed_len_sq); vert_lens[medge[i].v2] = min_ff(vert_lens[medge[i].v2], ed_len_sq); } } if (ofs_new != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; scalar_short = scalar_short_vgroup = ofs_new / 32767.0f; INIT_VERT_ARRAY_OFFSETS(false); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short; } if (do_clamp) { /* always reset becaise we may have set before */ if (dvert == NULL) { scalar_short_vgroup = scalar_short; } if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; scalar_short_vgroup *= scalar; } } madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); } } if (ofs_orig != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f; /* as above but swapped */ INIT_VERT_ARRAY_OFFSETS(true); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short; } if (do_clamp) { /* always reset becaise we may have set before */ if (dvert == NULL) { scalar_short_vgroup = scalar_short; } if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; scalar_short_vgroup *= scalar; } } madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); } } if (do_clamp) { MEM_freeN(vert_lens); } } else { #ifdef USE_NONMANIFOLD_WORKAROUND const bool check_non_manifold = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) != 0; #endif /* 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; unsigned int vidx; unsigned int i; if (vert_nors == NULL) { vert_nors = MEM_mallocN(sizeof(float) * numVerts * 3, "mod_solid_vno"); for (i = 0, mv = mvert; i < numVerts; i++, mv++) { normal_short_to_float_v3(vert_nors[i], mv->no); } } for (i = 0, mp = mpoly; i < numFaces; i++, mp++) { /* #BKE_mesh_calc_poly_angles logic is inlined here */ float nor_prev[3]; float nor_next[3]; int i_curr = mp->totloop - 1; int i_next = 0; ml = &mloop[mp->loopstart]; sub_v3_v3v3(nor_prev, mvert[ml[i_curr - 1].v].co, mvert[ml[i_curr].v].co); normalize_v3(nor_prev); while (i_next < mp->totloop) { float angle; sub_v3_v3v3(nor_next, mvert[ml[i_curr].v].co, mvert[ml[i_next].v].co); normalize_v3(nor_next); angle = angle_normalized_v3v3(nor_prev, nor_next); /* --- not related to angle calc --- */ if (angle < FLT_EPSILON) { angle = FLT_EPSILON; } vidx = ml[i_curr].v; vert_accum[vidx] += angle; #ifdef USE_NONMANIFOLD_WORKAROUND /* skip 3+ face user edges */ if ((check_non_manifold == false) || LIKELY(((orig_medge[ml[i_curr].e].flag & ME_EDGE_TMP_TAG) == 0) && ((orig_medge[ml[i_next].e].flag & ME_EDGE_TMP_TAG) == 0))) { vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_nors[i]) * angle; } else { vert_angles[vidx] += angle; } #else vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_nors[i]) * angle; #endif /* --- end non-angle-calc section --- */ /* step */ copy_v3_v3(nor_prev, nor_next); i_curr = i_next; i_next++; } } /* vertex group support */ if (dvert) { MDeformVert *dv = dvert; float scalar; if (defgrp_invert) { for (i = 0; i < numVerts; i++, dv++) { scalar = 1.0f - defvert_find_weight(dv, defgrp_index); scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); vert_angles[i] *= scalar; } } else { for (i = 0; i < numVerts; i++, dv++) { scalar = defvert_find_weight(dv, defgrp_index); scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); vert_angles[i] *= scalar; } } } if (do_clamp) { float *vert_lens_sq = MEM_mallocN(sizeof(float) * numVerts, "vert_lens"); const float offset = fabsf(smd->offset) * smd->offset_clamp; const float offset_sq = offset * offset; copy_vn_fl(vert_lens_sq, (int)numVerts, FLT_MAX); for (i = 0; i < numEdges; i++) { const float ed_len = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); vert_lens_sq[medge[i].v1] = min_ff(vert_lens_sq[medge[i].v1], ed_len); vert_lens_sq[medge[i].v2] = min_ff(vert_lens_sq[medge[i].v2], ed_len); } for (i = 0; i < numVerts; i++) { if (vert_lens_sq[i] < offset_sq) { float scalar = sqrtf(vert_lens_sq[i]) / offset; vert_angles[i] *= scalar; } } MEM_freeN(vert_lens_sq); } if (ofs_new != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; INIT_VERT_ARRAY_OFFSETS(false); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (vert_accum[i_other]) { /* zero if unselected */ madd_v3_v3fl(mv->co, vert_nors[i_other], ofs_new * (vert_angles[i_other] / vert_accum[i_other])); } } } if (ofs_orig != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; /* same as above but swapped, intentional use of 'ofs_new' */ INIT_VERT_ARRAY_OFFSETS(true); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (vert_accum[i_other]) { /* zero if unselected */ madd_v3_v3fl(mv->co, vert_nors[i_other], ofs_orig * (vert_angles[i_other] / vert_accum[i_other])); } } } MEM_freeN(vert_angles); } if (vert_nors) MEM_freeN(vert_nors); /* must recalculate normals with vgroups since they can displace unevenly [#26888] */ if ((dm->dirty & DM_DIRTY_NORMALS) || (smd->flag & MOD_SOLIDIFY_RIM) || dvert) { result->dirty |= DM_DIRTY_NORMALS; } else if (do_shell) { unsigned int i; /* flip vertex normals for copied verts */ mv = mvert + numVerts; for (i = 0; i < numVerts; i++, mv++) { negate_v3_short(mv->no); } } if (smd->flag & MOD_SOLIDIFY_RIM) { unsigned int i; /* 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 const bool do_side_normals = !(result->dirty & DM_DIRTY_NORMALS); /* annoying to allocate these since we only need the edge verts, */ float (*edge_vert_nos)[3] = do_side_normals ? MEM_callocN(sizeof(float) * numVerts * 3, __func__) : NULL; float nor[3]; #endif 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; int *origindex_edge; int *orig_ed; unsigned int j; if (crease_rim || crease_outer || crease_inner) { result->cd_flag |= ME_CDFLAG_EDGE_CREASE; } /* add faces & edges */ origindex_edge = result->getEdgeDataArray(result, CD_ORIGINDEX); ed = &medge[(numEdges * stride) + newEdges]; /* start after copied edges */ orig_ed = &origindex_edge[(numEdges * stride) + newEdges]; for (i = 0; i < rimVerts; i++, ed++, orig_ed++) { ed->v1 = new_vert_arr[i]; ed->v2 = (do_shell ? new_vert_arr[i] : i) + numVerts; ed->flag |= ME_EDGEDRAW; *orig_ed = ORIGINDEX_NONE; if (crease_rim) { ed->crease = crease_rim; } } /* faces */ mp = mpoly + (numFaces * stride); ml = mloop + (numLoops * stride); j = 0; for (i = 0; i < newFaces; i++, mp++) { unsigned int eidx = new_edge_arr[i]; unsigned int fidx = edge_users[eidx]; int k1, k2; bool flip; if (fidx >= numFaces) { fidx -= numFaces; flip = true; } else { flip = false; } ed = medge + eidx; /* copy most of the face settings */ DM_copy_poly_data(dm, result, (int)fidx, (int)((numFaces * stride) + i), 1); mp->loopstart = (int)(j + (numLoops * stride)); mp->flag = mpoly[fidx].flag; /* notice we use 'mp->totloop' which is later overwritten, * we could lookup the original face but theres no point since this is a copy * and will have the same value, just take care when changing order of assignment */ k1 = mpoly[fidx].loopstart + (((edge_order[eidx] - 1) + mp->totloop) % mp->totloop); /* prev loop */ k2 = mpoly[fidx].loopstart + (edge_order[eidx]); mp->totloop = 4; CustomData_copy_data(&dm->loopData, &result->loopData, k2, (int)((numLoops * stride) + j + 0), 1); CustomData_copy_data(&dm->loopData, &result->loopData, k1, (int)((numLoops * stride) + j + 1), 1); CustomData_copy_data(&dm->loopData, &result->loopData, k1, (int)((numLoops * stride) + j + 2), 1); CustomData_copy_data(&dm->loopData, &result->loopData, k2, (int)((numLoops * stride) + j + 3), 1); if (flip == false) { ml[j].v = ed->v1; ml[j++].e = eidx; ml[j].v = ed->v2; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; ml[j++].e = (do_shell ? eidx : i) + numEdges; ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; } else { ml[j].v = ed->v2; ml[j++].e = eidx; ml[j].v = ed->v1; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; ml[j++].e = (do_shell ? eidx : i) + numEdges; ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; } origindex_edge[ml[j - 3].e] = ORIGINDEX_NONE; origindex_edge[ml[j - 1].e] = ORIGINDEX_NONE; /* use the next material index if option enabled */ if (mat_ofs_rim) { mp->mat_nr += mat_ofs_rim; CLAMP(mp->mat_nr, 0, mat_nr_max); } if (crease_outer) { /* crease += crease_outer; without wrapping */ char *cr = &(ed->crease); int tcr = *cr + crease_outer; *cr = tcr > 255 ? 255 : tcr; } if (crease_inner) { /* crease += crease_inner; without wrapping */ char *cr = &(medge[numEdges + (do_shell ? eidx : i)].crease); int tcr = *cr + crease_inner; *cr = tcr > 255 ? 255 : tcr; } #ifdef SOLIDIFY_SIDE_NORMALS if (do_side_normals) { 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); add_v3_v3(edge_vert_nos[ed->v1], nor); add_v3_v3(edge_vert_nos[ed->v2], nor); } #endif } #ifdef SOLIDIFY_SIDE_NORMALS if (do_side_normals) { ed = medge + (numEdges * stride); for (i = 0; i < rimVerts; i++, ed++) { float nor_cpy[3]; short *nor_short; int k; /* note, only the first vertex (lower half of the index) is calculated */ normalize_v3_v3(nor_cpy, edge_vert_nos[ed->v1]); for (k = 0; k < 2; k++) { /* loop over both verts of the edge */ nor_short = mvert[*(&ed->v1 + k)].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); } if (old_vert_arr) MEM_freeN(old_vert_arr); if (face_nors) MEM_freeN(face_nors); if (numFaces == 0 && numEdges != 0) { modifier_setError(md, "Faces needed for useful output"); } return result; }
static DerivedMesh *applyModifier( ModifierData *md, Object *ob, DerivedMesh *dm, ModifierApplyFlag UNUSED(flag)) { MaskModifierData *mmd = (MaskModifierData *)md; const bool found_test = (mmd->flag & MOD_MASK_INV) == 0; DerivedMesh *result = NULL; GHash *vertHash = NULL, *edgeHash, *polyHash; GHashIterator gh_iter; MDeformVert *dvert, *dv; int numPolys = 0, numLoops = 0, numEdges = 0, numVerts = 0; int maxVerts, maxEdges, maxPolys; int i; const MVert *mvert_src; const MEdge *medge_src; const MPoly *mpoly_src; const MLoop *mloop_src; MPoly *mpoly_dst; MLoop *mloop_dst; MEdge *medge_dst; MVert *mvert_dst; int *loop_mapping; dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); if (dvert == NULL) { return found_test ? CDDM_from_template(dm, 0, 0, 0, 0, 0) : dm; } /* Overview of Method: * 1. Get the vertices that are in the vertexgroup of interest * 2. Filter out unwanted geometry (i.e. not in vertexgroup), by populating mappings with new vs old indices * 3. Make a new mesh containing only the mapping data */ /* get original number of verts, edges, and faces */ maxVerts = dm->getNumVerts(dm); maxEdges = dm->getNumEdges(dm); maxPolys = dm->getNumPolys(dm); /* check if we can just return the original mesh * - must have verts and therefore verts assigned to vgroups to do anything useful */ if (!(ELEM(mmd->mode, MOD_MASK_MODE_ARM, MOD_MASK_MODE_VGROUP)) || (maxVerts == 0) || BLI_listbase_is_empty(&ob->defbase)) { return dm; } /* if mode is to use selected armature bones, aggregate the bone groups */ if (mmd->mode == MOD_MASK_MODE_ARM) { /* --- using selected bones --- */ Object *oba = mmd->ob_arm; bPoseChannel *pchan; bDeformGroup *def; bool *bone_select_array; int bone_select_tot = 0; const int defbase_tot = BLI_listbase_count(&ob->defbase); /* check that there is armature object with bones to use, otherwise return original mesh */ if (ELEM(NULL, oba, oba->pose, ob->defbase.first)) return dm; /* determine whether each vertexgroup is associated with a selected bone or not * - each cell is a boolean saying whether bone corresponding to the ith group is selected * - groups that don't match a bone are treated as not existing (along with the corresponding ungrouped verts) */ bone_select_array = MEM_malloc_arrayN((size_t)defbase_tot, sizeof(char), "mask array"); for (i = 0, def = ob->defbase.first; def; def = def->next, i++) { pchan = BKE_pose_channel_find_name(oba->pose, def->name); if (pchan && pchan->bone && (pchan->bone->flag & BONE_SELECTED)) { bone_select_array[i] = true; bone_select_tot++; } else { bone_select_array[i] = false; } } /* verthash gives mapping from original vertex indices to the new indices (including selected matches only) * key = oldindex, value = newindex */ vertHash = BLI_ghash_int_new_ex("mask vert gh", (unsigned int)maxVerts); /* add vertices which exist in vertexgroups into vertHash for filtering * - dv = for each vertex, what vertexgroups does it belong to * - dw = weight that vertex was assigned to a vertexgroup it belongs to */ for (i = 0, dv = dvert; i < maxVerts; i++, dv++) { MDeformWeight *dw = dv->dw; bool found = false; int j; /* check the groups that vertex is assigned to, and see if it was any use */ for (j = 0; j < dv->totweight; j++, dw++) { if (dw->def_nr < defbase_tot) { if (bone_select_array[dw->def_nr]) { if (dw->weight != 0.0f) { found = true; break; } } } } if (found_test != found) { continue; } /* add to ghash for verts (numVerts acts as counter for mapping) */ BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numVerts)); numVerts++; } /* free temp hashes */ MEM_freeN(bone_select_array); } else { /* --- Using Nominated VertexGroup only --- */ int defgrp_index = defgroup_name_index(ob, mmd->vgroup); /* if no vgroup (i.e. dverts) found, return the initial mesh */ if (defgrp_index == -1) return dm; /* hashes for quickly providing a mapping from old to new - use key=oldindex, value=newindex */ vertHash = BLI_ghash_int_new_ex("mask vert2 bh", (unsigned int)maxVerts); /* add vertices which exist in vertexgroup into ghash for filtering */ for (i = 0, dv = dvert; i < maxVerts; i++, dv++) { const bool found = defvert_find_weight(dv, defgrp_index) != 0.0f; if (found_test != found) { continue; } /* add to ghash for verts (numVerts acts as counter for mapping) */ BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numVerts)); numVerts++; } } /* hashes for quickly providing a mapping from old to new - use key=oldindex, value=newindex */ edgeHash = BLI_ghash_int_new_ex("mask ed2 gh", (unsigned int)maxEdges); polyHash = BLI_ghash_int_new_ex("mask fa2 gh", (unsigned int)maxPolys); mvert_src = dm->getVertArray(dm); medge_src = dm->getEdgeArray(dm); mpoly_src = dm->getPolyArray(dm); mloop_src = dm->getLoopArray(dm); /* overalloc, assume all polys are seen */ loop_mapping = MEM_malloc_arrayN((size_t)maxPolys, sizeof(int), "mask loopmap"); /* loop over edges and faces, and do the same thing to * ensure that they only reference existing verts */ for (i = 0; i < maxEdges; i++) { const MEdge *me = &medge_src[i]; /* only add if both verts will be in new mesh */ if (BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v1)) && BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v2))) { BLI_ghash_insert(edgeHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numEdges)); numEdges++; } } for (i = 0; i < maxPolys; i++) { const MPoly *mp_src = &mpoly_src[i]; const MLoop *ml_src = &mloop_src[mp_src->loopstart]; bool ok = true; int j; for (j = 0; j < mp_src->totloop; j++, ml_src++) { if (!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(ml_src->v))) { ok = false; break; } } /* all verts must be available */ if (ok) { BLI_ghash_insert(polyHash, SET_INT_IN_POINTER(i), SET_INT_IN_POINTER(numPolys)); loop_mapping[numPolys] = numLoops; numPolys++; numLoops += mp_src->totloop; } } /* now we know the number of verts, edges and faces, * we can create the new (reduced) mesh */ result = CDDM_from_template(dm, numVerts, numEdges, 0, numLoops, numPolys); mpoly_dst = CDDM_get_polys(result); mloop_dst = CDDM_get_loops(result); medge_dst = CDDM_get_edges(result); mvert_dst = CDDM_get_verts(result); /* using ghash-iterators, map data into new mesh */ /* vertices */ GHASH_ITER (gh_iter, vertHash) { const MVert *v_src; MVert *v_dst; const int i_src = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(&gh_iter)); const int i_dst = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); v_src = &mvert_src[i_src]; v_dst = &mvert_dst[i_dst]; *v_dst = *v_src; DM_copy_vert_data(dm, result, i_src, i_dst, 1); } /* edges */ GHASH_ITER (gh_iter, edgeHash) { const MEdge *e_src; MEdge *e_dst; const int i_src = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(&gh_iter)); const int i_dst = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); e_src = &medge_src[i_src]; e_dst = &medge_dst[i_dst]; DM_copy_edge_data(dm, result, i_src, i_dst, 1); *e_dst = *e_src; e_dst->v1 = GET_UINT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_UINT_IN_POINTER(e_src->v1))); e_dst->v2 = GET_UINT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_UINT_IN_POINTER(e_src->v2))); } /* faces */ GHASH_ITER (gh_iter, polyHash) { const int i_src = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(&gh_iter)); const int i_dst = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); const MPoly *mp_src = &mpoly_src[i_src]; MPoly *mp_dst = &mpoly_dst[i_dst]; const int i_ml_src = mp_src->loopstart; const int i_ml_dst = loop_mapping[i_dst]; const MLoop *ml_src = &mloop_src[i_ml_src]; MLoop *ml_dst = &mloop_dst[i_ml_dst]; DM_copy_poly_data(dm, result, i_src, i_dst, 1); DM_copy_loop_data(dm, result, i_ml_src, i_ml_dst, mp_src->totloop); *mp_dst = *mp_src; mp_dst->loopstart = i_ml_dst; for (i = 0; i < mp_src->totloop; i++) { ml_dst[i].v = GET_UINT_FROM_POINTER(BLI_ghash_lookup(vertHash, SET_UINT_IN_POINTER(ml_src[i].v))); ml_dst[i].e = GET_UINT_FROM_POINTER(BLI_ghash_lookup(edgeHash, SET_UINT_IN_POINTER(ml_src[i].e))); } } MEM_freeN(loop_mapping); /* why is this needed? - campbell */ /* recalculate normals */ result->dirty |= DM_DIRTY_NORMALS; /* free hashes */ BLI_ghash_free(vertHash, NULL, NULL); BLI_ghash_free(edgeHash, NULL, NULL); BLI_ghash_free(polyHash, NULL, NULL); /* return the new mesh */ return result; }
static DerivedMesh *arrayModifier_doArray( ArrayModifierData *amd, Scene *scene, Object *ob, DerivedMesh *dm, ModifierApplyFlag flag) { const float eps = 1e-6f; const MVert *src_mvert; MVert *mv, *mv_prev, *result_dm_verts; MEdge *me; MLoop *ml; MPoly *mp; int i, j, c, count; float length = amd->length; /* offset matrix */ float offset[4][4]; float scale[3]; bool offset_has_scale; float current_offset[4][4]; float final_offset[4][4]; int *full_doubles_map = NULL; int tot_doubles; const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0; const bool use_recalc_normals = (dm->dirty & DM_DIRTY_NORMALS) || use_merge; const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob); int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_npolys = 0, start_cap_nloops = 0; int end_cap_nverts = 0, end_cap_nedges = 0, end_cap_npolys = 0, end_cap_nloops = 0; int result_nverts = 0, result_nedges = 0, result_npolys = 0, result_nloops = 0; int chunk_nverts, chunk_nedges, chunk_nloops, chunk_npolys; int first_chunk_start, first_chunk_nverts, last_chunk_start, last_chunk_nverts; DerivedMesh *result, *start_cap_dm = NULL, *end_cap_dm = NULL; int *vgroup_start_cap_remap = NULL; int vgroup_start_cap_remap_len = 0; int *vgroup_end_cap_remap = NULL; int vgroup_end_cap_remap_len = 0; chunk_nverts = dm->getNumVerts(dm); chunk_nedges = dm->getNumEdges(dm); chunk_nloops = dm->getNumLoops(dm); chunk_npolys = dm->getNumPolys(dm); count = amd->count; if (amd->start_cap && amd->start_cap != ob && amd->start_cap->type == OB_MESH) { vgroup_start_cap_remap = BKE_object_defgroup_index_map_create(amd->start_cap, ob, &vgroup_start_cap_remap_len); start_cap_dm = get_dm_for_modifier(amd->start_cap, flag); if (start_cap_dm) { start_cap_nverts = start_cap_dm->getNumVerts(start_cap_dm); start_cap_nedges = start_cap_dm->getNumEdges(start_cap_dm); start_cap_nloops = start_cap_dm->getNumLoops(start_cap_dm); start_cap_npolys = start_cap_dm->getNumPolys(start_cap_dm); } } if (amd->end_cap && amd->end_cap != ob && amd->end_cap->type == OB_MESH) { vgroup_end_cap_remap = BKE_object_defgroup_index_map_create(amd->end_cap, ob, &vgroup_end_cap_remap_len); end_cap_dm = get_dm_for_modifier(amd->end_cap, flag); if (end_cap_dm) { end_cap_nverts = end_cap_dm->getNumVerts(end_cap_dm); end_cap_nedges = end_cap_dm->getNumEdges(end_cap_dm); end_cap_nloops = end_cap_dm->getNumLoops(end_cap_dm); end_cap_npolys = end_cap_dm->getNumPolys(end_cap_dm); } } /* Build up offset array, cumulating all settings options */ unit_m4(offset); src_mvert = dm->getVertArray(dm); if (amd->offset_type & MOD_ARR_OFF_CONST) { add_v3_v3(offset[3], amd->offset); } if (amd->offset_type & MOD_ARR_OFF_RELATIVE) { float min[3], max[3]; const MVert *src_mv; INIT_MINMAX(min, max); for (src_mv = src_mvert, j = chunk_nverts; j--; src_mv++) { minmax_v3v3_v3(min, max, src_mv->co); } for (j = 3; j--; ) { offset[3][j] += amd->scale[j] * (max[j] - min[j]); } } if (use_offset_ob) { float obinv[4][4]; float result_mat[4][4]; if (ob) invert_m4_m4(obinv, ob->obmat); else unit_m4(obinv); mul_m4_series(result_mat, offset, obinv, amd->offset_ob->obmat); copy_m4_m4(offset, result_mat); } /* Check if there is some scaling. If scaling, then we will not translate mapping */ mat4_to_size(scale, offset); offset_has_scale = !is_one_v3(scale); if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob) { Curve *cu = amd->curve_ob->data; if (cu) { #ifdef CYCLIC_DEPENDENCY_WORKAROUND if (amd->curve_ob->curve_cache == NULL) { BKE_displist_make_curveTypes(scene, amd->curve_ob, false); } #endif if (amd->curve_ob->curve_cache && amd->curve_ob->curve_cache->path) { float scale_fac = mat4_to_scale(amd->curve_ob->obmat); length = scale_fac * amd->curve_ob->curve_cache->path->totdist; } } } /* calculate the maximum number of copies which will fit within the * prescribed length */ if (amd->fit_type == MOD_ARR_FITLENGTH || amd->fit_type == MOD_ARR_FITCURVE) { float dist = len_v3(offset[3]); if (dist > eps) { /* this gives length = first copy start to last copy end * add a tiny offset for floating point rounding errors */ count = (length + eps) / dist + 1; } else { /* if the offset has no translation, just make one copy */ count = 1; } } if (count < 1) count = 1; /* The number of verts, edges, loops, polys, before eventually merging doubles */ result_nverts = chunk_nverts * count + start_cap_nverts + end_cap_nverts; result_nedges = chunk_nedges * count + start_cap_nedges + end_cap_nedges; result_nloops = chunk_nloops * count + start_cap_nloops + end_cap_nloops; result_npolys = chunk_npolys * count + start_cap_npolys + end_cap_npolys; /* Initialize a result dm */ result = CDDM_from_template(dm, result_nverts, result_nedges, 0, result_nloops, result_npolys); result_dm_verts = CDDM_get_verts(result); if (use_merge) { /* Will need full_doubles_map for handling merge */ full_doubles_map = MEM_malloc_arrayN(result_nverts, sizeof(int), "mod array doubles map"); copy_vn_i(full_doubles_map, result_nverts, -1); } /* copy customdata to original geometry */ DM_copy_vert_data(dm, result, 0, 0, chunk_nverts); DM_copy_edge_data(dm, result, 0, 0, chunk_nedges); DM_copy_loop_data(dm, result, 0, 0, chunk_nloops); DM_copy_poly_data(dm, result, 0, 0, chunk_npolys); /* Subsurf for eg won't have mesh data in the custom data arrays. * now add mvert/medge/mpoly layers. */ if (!CustomData_has_layer(&dm->vertData, CD_MVERT)) { dm->copyVertArray(dm, result_dm_verts); } if (!CustomData_has_layer(&dm->edgeData, CD_MEDGE)) { dm->copyEdgeArray(dm, CDDM_get_edges(result)); } if (!CustomData_has_layer(&dm->polyData, CD_MPOLY)) { dm->copyLoopArray(dm, CDDM_get_loops(result)); dm->copyPolyArray(dm, CDDM_get_polys(result)); } /* Remember first chunk, in case of cap merge */ first_chunk_start = 0; first_chunk_nverts = chunk_nverts; unit_m4(current_offset); for (c = 1; c < count; c++) { /* copy customdata to new geometry */ DM_copy_vert_data(result, result, 0, c * chunk_nverts, chunk_nverts); DM_copy_edge_data(result, result, 0, c * chunk_nedges, chunk_nedges); DM_copy_loop_data(result, result, 0, c * chunk_nloops, chunk_nloops); DM_copy_poly_data(result, result, 0, c * chunk_npolys, chunk_npolys); mv_prev = result_dm_verts; mv = mv_prev + c * chunk_nverts; /* recalculate cumulative offset here */ mul_m4_m4m4(current_offset, current_offset, offset); /* apply offset to all new verts */ for (i = 0; i < chunk_nverts; i++, mv++, mv_prev++) { mul_m4_v3(current_offset, mv->co); /* We have to correct normals too, if we do not tag them as dirty! */ if (!use_recalc_normals) { float no[3]; normal_short_to_float_v3(no, mv->no); mul_mat3_m4_v3(current_offset, no); normalize_v3(no); normal_float_to_short_v3(mv->no, no); } } /* adjust edge vertex indices */ me = CDDM_get_edges(result) + c * chunk_nedges; for (i = 0; i < chunk_nedges; i++, me++) { me->v1 += c * chunk_nverts; me->v2 += c * chunk_nverts; } mp = CDDM_get_polys(result) + c * chunk_npolys; for (i = 0; i < chunk_npolys; i++, mp++) { mp->loopstart += c * chunk_nloops; } /* adjust loop vertex and edge indices */ ml = CDDM_get_loops(result) + c * chunk_nloops; for (i = 0; i < chunk_nloops; i++, ml++) { ml->v += c * chunk_nverts; ml->e += c * chunk_nedges; } /* Handle merge between chunk n and n-1 */ if (use_merge && (c >= 1)) { if (!offset_has_scale && (c >= 2)) { /* Mapping chunk 3 to chunk 2 is a translation of mapping 2 to 1 * ... that is except if scaling makes the distance grow */ int k; int this_chunk_index = c * chunk_nverts; int prev_chunk_index = (c - 1) * chunk_nverts; for (k = 0; k < chunk_nverts; k++, this_chunk_index++, prev_chunk_index++) { int target = full_doubles_map[prev_chunk_index]; if (target != -1) { target += chunk_nverts; /* translate mapping */ while (target != -1 && !ELEM(full_doubles_map[target], -1, target)) { /* If target is already mapped, we only follow that mapping if final target remains * close enough from current vert (otherwise no mapping at all). */ if (compare_len_v3v3(result_dm_verts[this_chunk_index].co, result_dm_verts[full_doubles_map[target]].co, amd->merge_dist)) { target = full_doubles_map[target]; } else { target = -1; } } } full_doubles_map[this_chunk_index] = target; } } else { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, (c - 1) * chunk_nverts, chunk_nverts, c * chunk_nverts, chunk_nverts, amd->merge_dist); } } } /* handle UVs */ if (chunk_nloops > 0 && is_zero_v2(amd->uv_offset) == false) { const int totuv = CustomData_number_of_layers(&result->loopData, CD_MLOOPUV); for (i = 0; i < totuv; i++) { MLoopUV *dmloopuv = CustomData_get_layer_n(&result->loopData, CD_MLOOPUV, i); dmloopuv += chunk_nloops; for (c = 1; c < count; c++) { const float uv_offset[2] = { amd->uv_offset[0] * (float)c, amd->uv_offset[1] * (float)c, }; int l_index = chunk_nloops; for (; l_index-- != 0; dmloopuv++) { dmloopuv->uv[0] += uv_offset[0]; dmloopuv->uv[1] += uv_offset[1]; } } } } last_chunk_start = (count - 1) * chunk_nverts; last_chunk_nverts = chunk_nverts; copy_m4_m4(final_offset, current_offset); if (use_merge && (amd->flags & MOD_ARR_MERGEFINAL) && (count > 1)) { /* Merge first and last copies */ dm_mvert_map_doubles( full_doubles_map, result_dm_verts, last_chunk_start, last_chunk_nverts, first_chunk_start, first_chunk_nverts, amd->merge_dist); } /* start capping */ if (start_cap_dm) { float start_offset[4][4]; int start_cap_start = result_nverts - start_cap_nverts - end_cap_nverts; invert_m4_m4(start_offset, offset); dm_merge_transform( result, start_cap_dm, start_offset, result_nverts - start_cap_nverts - end_cap_nverts, result_nedges - start_cap_nedges - end_cap_nedges, result_nloops - start_cap_nloops - end_cap_nloops, result_npolys - start_cap_npolys - end_cap_npolys, start_cap_nverts, start_cap_nedges, start_cap_nloops, start_cap_npolys, vgroup_start_cap_remap, vgroup_start_cap_remap_len); /* Identify doubles with first chunk */ if (use_merge) { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, first_chunk_start, first_chunk_nverts, start_cap_start, start_cap_nverts, amd->merge_dist); } } if (end_cap_dm) { float end_offset[4][4]; int end_cap_start = result_nverts - end_cap_nverts; mul_m4_m4m4(end_offset, current_offset, offset); dm_merge_transform( result, end_cap_dm, end_offset, result_nverts - end_cap_nverts, result_nedges - end_cap_nedges, result_nloops - end_cap_nloops, result_npolys - end_cap_npolys, end_cap_nverts, end_cap_nedges, end_cap_nloops, end_cap_npolys, vgroup_end_cap_remap, vgroup_end_cap_remap_len); /* Identify doubles with last chunk */ if (use_merge) { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, last_chunk_start, last_chunk_nverts, end_cap_start, end_cap_nverts, amd->merge_dist); } } /* done capping */ /* Handle merging */ tot_doubles = 0; if (use_merge) { for (i = 0; i < result_nverts; i++) { int new_i = full_doubles_map[i]; if (new_i != -1) { /* We have to follow chains of doubles (merge start/end especially is likely to create some), * those are not supported at all by CDDM_merge_verts! */ while (!ELEM(full_doubles_map[new_i], -1, new_i)) { new_i = full_doubles_map[new_i]; } if (i == new_i) { full_doubles_map[i] = -1; } else { full_doubles_map[i] = new_i; tot_doubles++; } } } if (tot_doubles > 0) { result = CDDM_merge_verts(result, full_doubles_map, tot_doubles, CDDM_MERGE_VERTS_DUMP_IF_EQUAL); } MEM_freeN(full_doubles_map); } /* In case org dm has dirty normals, or we made some merging, mark normals as dirty in new dm! * TODO: we may need to set other dirty flags as well? */ if (use_recalc_normals) { result->dirty |= DM_DIRTY_NORMALS; } if (vgroup_start_cap_remap) { MEM_freeN(vgroup_start_cap_remap); } if (vgroup_end_cap_remap) { MEM_freeN(vgroup_end_cap_remap); } return result; }