diff options
Diffstat (limited to 'source/blender/modifiers/intern/MOD_array.c')
-rw-r--r-- | source/blender/modifiers/intern/MOD_array.c | 307 |
1 files changed, 216 insertions, 91 deletions
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 621be8023a2..d552e73bc32 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -162,6 +162,51 @@ static float vertarray_size(MVert *mvert, int numVerts, int axis) return max_co - min_co; } +static int *find_doubles_index_map(BMesh *bm, BMOperator *dupe_op, + const ArrayModifierData *amd, + int *index_map_length) +{ + BMOperator find_op; + BMOIter oiter; + BMVert *v, *v2; + BMElem *ele; + int *index_map, i; + + BMO_op_initf(bm, &find_op, + "finddoubles verts=%av dist=%f keepverts=%s", + amd->merge_dist, dupe_op, "geom"); + + BMO_op_exec(bm, &find_op); + + i = 0; + BMO_ITER(ele, &oiter, bm, dupe_op, "geom", BM_ALL) { + BM_elem_index_set(ele, i); /* set_dirty */ + i++; + } + + BMO_ITER(ele, &oiter, bm, dupe_op, "newout", BM_ALL) { + BM_elem_index_set(ele, i); /* set_dirty */ + i++; + } + /* above loops over all, so set all to dirty, if this is somehow + * setting valid values, this line can be remvoed - campbell */ + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + (*index_map_length) = i; + index_map = MEM_callocN(sizeof(int) * (*index_map_length), "index_map"); + + /*element type argument doesn't do anything here*/ + BMO_ITER(v, &oiter, bm, &find_op, "targetmapout", 0) { + v2 = BMO_iter_map_value_p(&oiter); + + index_map[BM_elem_index_get(v)] = BM_elem_index_get(v2) + 1; + } + + BMO_op_finish(bm, &find_op); + + return index_map; +} + /* Used for start/end cap. * * this function expects all existing vertices to be tagged, @@ -169,29 +214,97 @@ static float vertarray_size(MVert *mvert, int numVerts, int axis) * * All verts will be tagged on exit. */ -static void bm_merge_dm_transform(BMesh* bm, DerivedMesh *dm, float mat[4][4]) +static void bm_merge_dm_transform(BMesh* bm, DerivedMesh *dm, float mat[4][4], + const ArrayModifierData *amd, + BMOperator *dupe_op, + const char *dupe_slot_name, + BMOperator *weld_op) { - BMVert *v; + BMVert *v, *v2; BMIter iter; DM_to_bmesh_ex(dm, bm); - /* transform all verts */ - BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { - if (!BM_elem_flag_test(v, BM_ELEM_TAG)) { - mul_m4_v3(mat, v->co); - BM_elem_flag_enable(v, BM_ELEM_TAG); + if (amd->flags & MOD_ARR_MERGE) { + /* if merging is enabled, find doubles */ + + BMOIter oiter; + BMOperator find_op; + + BMO_op_initf(bm, &find_op, + "finddoubles verts=%Hv dist=%f keepverts=%s", + BM_ELEM_TAG, amd->merge_dist, + dupe_op, dupe_slot_name); + + /* append the dupe's geom to the findop input verts */ + BMO_slot_buffer_append(&find_op, "verts", dupe_op, dupe_slot_name); + + /* transform and tag verts */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (!BM_elem_flag_test(v, BM_ELEM_TAG)) { + mul_m4_v3(mat, v->co); + BM_elem_flag_enable(v, BM_ELEM_TAG); + } + } + + BMO_op_exec(bm, &find_op); + + /* add new merge targets to weld operator */ + BMO_ITER(v, &oiter, bm, &find_op, "targetmapout", 0) { + v2 = BMO_iter_map_value_p(&oiter); + BMO_slot_map_ptr_insert(bm, weld_op, "targetmap", v, v2); + } + + BMO_op_finish(bm, &find_op); + } + else { + /* transform and tag verts */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (!BM_elem_flag_test(v, BM_ELEM_TAG)) { + mul_m4_v3(mat, v->co); + BM_elem_flag_enable(v, BM_ELEM_TAG); + } } } } +static void merge_first_last(BMesh* bm, + const ArrayModifierData *amd, + BMOperator *dupe_first, + BMOperator *dupe_last, + BMOperator *weld_op) +{ + BMOperator find_op; + BMOIter oiter; + BMVert *v, *v2; + + BMO_op_initf(bm, &find_op, + "finddoubles verts=%s dist=%f keepverts=%s", + dupe_first, "geom", amd->merge_dist, + dupe_first, "geom"); + + /* append the last dupe's geom to the findop input verts */ + BMO_slot_buffer_append(&find_op, "verts", dupe_last, "newout"); + + BMO_op_exec(bm, &find_op); + + /* add new merge targets to weld operator */ + BMO_ITER(v, &oiter, bm, &find_op, "targetmapout", 0) { + v2 = BMO_iter_map_value_p(&oiter); + BMO_slot_map_ptr_insert(bm, weld_op, "targetmap", v, v2); + } + + BMO_op_finish(bm, &find_op); +} + static DerivedMesh *arrayModifier_doArray(ArrayModifierData *amd, Scene *scene, Object *ob, DerivedMesh *dm, int UNUSED(initFlags)) { DerivedMesh *result; BMEditMesh *em = DM_to_editbmesh(dm, NULL, FALSE); - BMOperator op, oldop, weldop; + BMOperator first_dupe_op, dupe_op, old_dupe_op, weld_op; + BMVert **first_geom = NULL; int i, j, indexLen; /* offset matrix */ float offset[4][4]; @@ -237,14 +350,6 @@ static DerivedMesh *arrayModifier_doArray(ArrayModifierData *amd, copy_m4_m4(offset, result_mat); } - /* calculate the offset matrix of the final copy (for merging) */ - unit_m4(final_offset); - - for (j=0; j < count - 1; j++) { - mult_m4_m4m4(tmp_mat, offset, final_offset); - copy_m4_m4(final_offset, tmp_mat); - } - if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob) { Curve *cu = amd->curve_ob->data; if (cu) { @@ -281,6 +386,14 @@ static DerivedMesh *arrayModifier_doArray(ArrayModifierData *amd, if (count < 1) count = 1; + /* calculate the offset matrix of the final copy (for merging) */ + unit_m4(final_offset); + + for (j=0; j < count - 1; j++) { + mult_m4_m4m4(tmp_mat, offset, final_offset); + copy_m4_m4(final_offset, tmp_mat); + } + /* BMESH_TODO: bumping up the stack level avoids computing the normals * after every top-level operator execution (and this modifier has the * potential to execute a *lot* of top-level BMOps. There should be a @@ -290,118 +403,118 @@ static DerivedMesh *arrayModifier_doArray(ArrayModifierData *amd, BMO_push(em->bm, NULL); bmesh_edit_begin(em->bm, 0); - BMO_op_init(em->bm, &weldop, "weldverts"); - BMO_op_initf(em->bm, &op, "dupe geom=%avef"); - oldop = op; - for (j=0; j < count - 1; j++) { - BMVert *v, *v2; - BMOpSlot *s1; - BMOpSlot *s2; - - BMO_op_initf(em->bm, &op, "dupe geom=%s", &oldop, j==0 ? "geom" : "newout"); - BMO_op_exec(em->bm, &op); + if (amd->flags & MOD_ARR_MERGE) + BMO_op_init(em->bm, &weld_op, "weldverts"); - s1 = BMO_slot_get(&op, "geom"); - s2 = BMO_slot_get(&op, "newout"); + BMO_op_initf(em->bm, &dupe_op, "dupe geom=%avef"); + first_dupe_op = dupe_op; - BMO_op_callf(em->bm, "transform mat=%m4 verts=%s", offset, &op, "newout"); + for (j=0; j < count - 1; j++) { + BMVert *v, *v2, *v3; + BMOpSlot *geom_slot; + BMOpSlot *newout_slot; + BMOIter oiter; - #define _E(s, i) ((BMVert **)(s)->data.buf)[i] + if (j != 0) + BMO_op_initf(em->bm, &dupe_op, "dupe geom=%s", &old_dupe_op, "newout"); + BMO_op_exec(em->bm, &dupe_op); - /*calculate merge mapping*/ - if (j == 0) { - BMOperator findop; - BMOIter oiter; - BMVert *v, *v2; - BMElem *ele; + geom_slot = BMO_slot_get(&dupe_op, "geom"); + newout_slot = BMO_slot_get(&dupe_op, "newout"); - BMO_op_initf(em->bm, &findop, - "finddoubles verts=%av dist=%f keepverts=%s", - amd->merge_dist, &op, "geom"); + if ((amd->flags & MOD_ARR_MERGEFINAL) && j == 0) { + int first_geom_bytes = sizeof(BMVert*) * geom_slot->len; + + /* make a copy of the initial geometry ordering so the + last duplicate can be merged into it */ + first_geom = MEM_mallocN(first_geom_bytes, "first_geom"); + memcpy(first_geom, geom_slot->data.buf, first_geom_bytes); + } - i = 0; - BMO_ITER(ele, &oiter, em->bm, &op, "geom", BM_ALL) { - BM_elem_index_set(ele, i); /* set_dirty */ - i++; - } + /* apply transformation matrix */ + BMO_ITER(v, &oiter, em->bm, &dupe_op, "newout", BM_VERT) { + mul_m4_v3(offset, v->co); + } - BMO_ITER(ele, &oiter, em->bm, &op, "newout", BM_ALL) { - BM_elem_index_set(ele, i); /* set_dirty */ - i++; + if (amd->flags & MOD_ARR_MERGE) { + /*calculate merge mapping*/ + if (j == 0) { + indexMap = find_doubles_index_map(em->bm, &dupe_op, + amd, &indexLen); } - /* above loops over all, so set all to dirty, if this is somehow - * setting valid values, this line can be remvoed - campbell */ - em->bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + #define _E(s, i) ((BMVert **)(s)->data.buf)[i] - BMO_op_exec(em->bm, &findop); + for (i=0; i<indexLen; i++) { + if (!indexMap[i]) continue; - indexLen = i; - indexMap = MEM_callocN(sizeof(int)*indexLen, "indexMap"); + /* merge v (from 'newout') into v2 (from old 'geom') */ + v = _E(newout_slot, i - geom_slot->len); + v2 = _E(geom_slot, indexMap[i]-1); - /*element type argument doesn't do anything here*/ - BMO_ITER(v, &oiter, em->bm, &findop, "targetmapout", 0) { - v2 = BMO_iter_map_value_p(&oiter); + /* check in case the target vertex (v2) is already marked + for merging */ + while((v3 = BMO_slot_map_ptr_get(em->bm, &weld_op, "targetmap", v2))) + v2 = v3; - indexMap[BM_elem_index_get(v)] = BM_elem_index_get(v2)+1; + BMO_slot_map_ptr_insert(em->bm, &weld_op, "targetmap", v, v2); } - BMO_op_finish(em->bm, &findop); + #undef _E } - /* generate merge mapping using index map. we do this by using the - * operator slots as lookup arrays.*/ - #define E(i) (i) < s1->len ? _E(s1, i) : _E(s2, (i)-s1->len) - - for (i=0; i<indexLen; i++) { - if (!indexMap[i]) continue; - - v = E(i); - v2 = E(indexMap[i]-1); - - BMO_slot_map_ptr_insert(em->bm, &weldop, "targetmap", v, v2); - } - - #undef E - #undef _E - - BMO_op_finish(em->bm, &oldop); - oldop = op; + /* already copied earlier, but after executation more slot + memory may be allocated */ + if (j == 0) + first_dupe_op = dupe_op; + + if (j >= 2) + BMO_op_finish(em->bm, &old_dupe_op); + old_dupe_op = dupe_op; } - if (j > 0) BMO_op_finish(em->bm, &op); + if ((amd->flags & MOD_ARR_MERGE) && + (amd->flags & MOD_ARR_MERGEFINAL) && + (count > 1)) { + /* Merge first and last copies. Note that we can't use the + indexMap for this because (unless the array is forming a + loop) the offset between first and last is different from + dupe X to dupe X+1. */ - /* BMESH_TODO - cap ends are not welded, even though weld is called after */ + merge_first_last(em->bm, amd, &first_dupe_op, &dupe_op, &weld_op); + } /* start capping */ - if ((start_cap || end_cap) && - - /* BMESH_TODO - theres a bug in DM_to_bmesh_ex() when in editmode! - * this needs investigation, but for now at least don't crash */ - ob->mode != OB_MODE_EDIT - - ) + if (start_cap || end_cap) { BM_mesh_elem_flag_enable_all(em->bm, BM_VERT, BM_ELEM_TAG); if (start_cap) { float startoffset[4][4]; invert_m4_m4(startoffset, offset); - bm_merge_dm_transform(em->bm, start_cap, startoffset); + bm_merge_dm_transform(em->bm, start_cap, startoffset, amd, + &first_dupe_op, "geom", &weld_op); } if (end_cap) { float endoffset[4][4]; mult_m4_m4m4(endoffset, offset, final_offset); - bm_merge_dm_transform(em->bm, end_cap, endoffset); + bm_merge_dm_transform(em->bm, end_cap, endoffset, amd, + &dupe_op, count == 1 ? "geom" : "newout", &weld_op); } } /* done capping */ - if (amd->flags & MOD_ARR_MERGE) - BMO_op_exec(em->bm, &weldop); + /* free remaining dupe operators */ + BMO_op_finish(em->bm, &first_dupe_op); + if (count > 2) + BMO_op_finish(em->bm, &dupe_op); - BMO_op_finish(em->bm, &weldop); + /* run merge operator */ + if (amd->flags & MOD_ARR_MERGE) { + BMO_op_exec(em->bm, &weld_op); + BMO_op_finish(em->bm, &weld_op); + } /* Bump the stack level back down to match the adjustment up above */ BMO_pop(em->bm); @@ -409,9 +522,21 @@ static DerivedMesh *arrayModifier_doArray(ArrayModifierData *amd, BLI_assert(em->looptris == NULL); result = CDDM_from_BMEditMesh(em, NULL, FALSE, FALSE); + if ((amd->offset_type & MOD_ARR_OFF_OBJ) && (amd->offset_ob)) { + /* Update normals in case offset object has rotation. */ + + /* BMESH_TODO: check if normal recalc needed under any other + conditions? */ + + CDDM_calc_normals(result); + } + BMEdit_Free(em); MEM_freeN(em); - MEM_freeN(indexMap); + if (indexMap) + MEM_freeN(indexMap); + if (first_geom) + MEM_freeN(first_geom); return result; } |