Changeset View
Standalone View
source/blender/modifiers/intern/MOD_weld.c
| Context not available. | |||||
| #include "MOD_modifiertypes.h" | #include "MOD_modifiertypes.h" | ||||
| #include "MOD_ui_common.h" | #include "MOD_ui_common.h" | ||||
| #include <strings.h> | |||||
| //#define USE_WELD_DEBUG | //#define USE_WELD_DEBUG | ||||
| //#define USE_WELD_NORMALS | //#define USE_WELD_NORMALS | ||||
| Context not available. | |||||
| uint indexA = overlap_iter->indexA; | uint indexA = overlap_iter->indexA; | ||||
| uint indexB = overlap_iter->indexB; | uint indexB = overlap_iter->indexB; | ||||
| uint va_dst = vert_dest_map[indexA]; | uint va_dst = vert_dest_map[indexA]; | ||||
| while (va_dst != vert_dest_map[va_dst]) { | |||||
| va_dst = vert_dest_map[va_dst]; | |||||
| } | |||||
| uint vb_dst = vert_dest_map[indexB]; | uint vb_dst = vert_dest_map[indexB]; | ||||
| while (vb_dst != vert_dest_map[vb_dst]) { | |||||
| vb_dst = vert_dest_map[vb_dst]; | |||||
| } | |||||
| BLI_assert(va_dst == vb_dst); | BLI_assert(va_dst == vb_dst); | ||||
| } | } | ||||
| Context not available. | |||||
| * \{ */ | * \{ */ | ||||
| static void weld_vert_ctx_alloc_and_setup(const uint mvert_len, | static void weld_vert_ctx_alloc_and_setup(const uint mvert_len, | ||||
| const BVHTreeOverlap *overlap, | |||||
| const uint overlap_len, | |||||
| uint *r_vert_dest_map, | uint *r_vert_dest_map, | ||||
| WeldVert **r_wvert, | WeldVert **r_wvert, | ||||
| uint *r_wvert_len, | uint *r_wvert_len) | ||||
| uint *r_vert_kill_len) | |||||
| { | { | ||||
| uint *v_dest_iter = &r_vert_dest_map[0]; | /* Fix #r_vert_dest_map for next step. */ | ||||
| for (uint i = mvert_len; i--; v_dest_iter++) { | for (uint i = 0; i < mvert_len; i++) { | ||||
| *v_dest_iter = OUT_OF_CONTEXT; | if (i == r_vert_dest_map[i]) { | ||||
| } | r_vert_dest_map[i] = OUT_OF_CONTEXT; | ||||
mano-wii: I'm not sure if `"weld_fix_vert_map"` is an appropriate name
A comment here would be useful to… | |||||
Done Inline ActionsAgreed. Just for reference here: The vert_dest_map prior to this function is supposed to converge to the correct value when iterated (for fixpoints). This function will finalize this map by doing the iteration for every vert and writing the result. It also sets all verts that didn't get merged to OUT_OF_CONTEXT. weasel: Agreed. Just for reference here: The `vert_dest_map` prior to this function is supposed to… | |||||
| uint vert_kill_len = 0; | |||||
| const BVHTreeOverlap *overlap_iter = &overlap[0]; | |||||
| for (uint i = 0; i < overlap_len; i++, overlap_iter++) { | |||||
| uint indexA = overlap_iter->indexA; | |||||
| uint indexB = overlap_iter->indexB; | |||||
| BLI_assert(indexA < indexB); | |||||
| uint va_dst = r_vert_dest_map[indexA]; | |||||
| uint vb_dst = r_vert_dest_map[indexB]; | |||||
| if (va_dst == OUT_OF_CONTEXT) { | |||||
| if (vb_dst == OUT_OF_CONTEXT) { | |||||
| vb_dst = indexA; | |||||
| r_vert_dest_map[indexB] = vb_dst; | |||||
| } | |||||
| r_vert_dest_map[indexA] = vb_dst; | |||||
| vert_kill_len++; | |||||
| } | } | ||||
| else if (vb_dst == OUT_OF_CONTEXT) { | else { | ||||
| r_vert_dest_map[indexB] = va_dst; | uint v = i; | ||||
| vert_kill_len++; | while (v != r_vert_dest_map[v] && r_vert_dest_map[v] != OUT_OF_CONTEXT) { | ||||
| } | v = r_vert_dest_map[v]; | ||||
| else if (va_dst != vb_dst) { | |||||
| uint v_new, v_old; | |||||
| if (va_dst < vb_dst) { | |||||
| v_new = va_dst; | |||||
| v_old = vb_dst; | |||||
| } | |||||
| else { | |||||
| v_new = vb_dst; | |||||
| v_old = va_dst; | |||||
| } | |||||
| BLI_assert(r_vert_dest_map[v_old] == v_old); | |||||
| BLI_assert(r_vert_dest_map[v_new] == v_new); | |||||
| vert_kill_len++; | |||||
| const BVHTreeOverlap *overlap_iter_b = &overlap[0]; | |||||
| for (uint j = i + 1; j--; overlap_iter_b++) { | |||||
| indexA = overlap_iter_b->indexA; | |||||
| indexB = overlap_iter_b->indexB; | |||||
| va_dst = r_vert_dest_map[indexA]; | |||||
| vb_dst = r_vert_dest_map[indexB]; | |||||
| if (ELEM(v_old, vb_dst, va_dst)) { | |||||
| r_vert_dest_map[indexA] = v_new; | |||||
| r_vert_dest_map[indexB] = v_new; | |||||
| } | |||||
| } | } | ||||
| BLI_assert(r_vert_dest_map[v_old] == v_new); | r_vert_dest_map[v] = v; | ||||
| r_vert_dest_map[i] = v; | |||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| wvert = MEM_mallocN(sizeof(*wvert) * mvert_len, __func__); | wvert = MEM_mallocN(sizeof(*wvert) * mvert_len, __func__); | ||||
| wv = &wvert[0]; | wv = &wvert[0]; | ||||
| v_dest_iter = &r_vert_dest_map[0]; | uint *v_dest_iter = &r_vert_dest_map[0]; | ||||
| for (uint i = 0; i < mvert_len; i++, v_dest_iter++) { | for (uint i = 0; i < mvert_len; i++, v_dest_iter++) { | ||||
| if (*v_dest_iter != OUT_OF_CONTEXT) { | if (*v_dest_iter != OUT_OF_CONTEXT) { | ||||
| wv->vert_dest = *v_dest_iter; | wv->vert_dest = *v_dest_iter; | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| #ifdef USE_WELD_DEBUG | |||||
| weld_assert_vert_dest_map_setup(overlap, overlap_len, r_vert_dest_map); | |||||
| #endif | |||||
| *r_wvert = MEM_reallocN(wvert, sizeof(*wvert) * wvert_len); | *r_wvert = MEM_reallocN(wvert, sizeof(*wvert) * wvert_len); | ||||
| *r_wvert_len = wvert_len; | *r_wvert_len = wvert_len; | ||||
| *r_vert_kill_len = vert_kill_len; | |||||
| } | } | ||||
Done Inline ActionsFrom what I can see here. Neither 'medge' nor 'totegde' are being used. mano-wii: From what I can see here. Neither 'medge' nor 'totegde' are being used. | |||||
| static void weld_vert_groups_setup(const uint mvert_len, | static void weld_vert_groups_setup(const uint mvert_len, | ||||
Done Inline ActionsRemove or change this unused parameter to uint UNUSED(v_mask_act). mano-wii: Remove or change this unused parameter to `uint UNUSED(v_mask_act)`.
This avoids warnings on… | |||||
Not Done Inline ActionsIs it really necessary to allocate these arrays mvert and combined_verts? Isn't the idea here just to detect the edges that collapse? mano-wii: Is it really necessary to allocate these arrays `mvert` and `combined_verts`?
Apparently the… | |||||
Done Inline ActionsThis came from complex solidify. Originally it didn't have that, but the chain merging was quite severe on meshes with ordered vertices like text. Honestly it is also quite bad with the usual weld modifier. D7224 changed that in solidify and I want to keep it like that. If you can think of a different way of getting rid of that chaining with a good result, propose it, but I found this (even though it is order dependend) is fairly good in practice and the order dependency does usually not become a problem (See T75032#896741 for the things I thought of how to solve this). Also the calculated positions could potentially be used as final positions in the future, removing some of the extra overhead. What I am more concerned with here is, that with very high merging thresholds various spikes appear. I know the reason for it, but it's not trivial to fix. weasel: This came from complex solidify. Originally it didn't have that, but the chain merging was… | |||||
| Context not available. | |||||
| /** \name Weld Mesh API | /** \name Weld Mesh API | ||||
| * \{ */ | * \{ */ | ||||
| static void weld_mesh_context_create(const Mesh *mesh, | static void weld_mesh_context_create(const Mesh *mesh, WeldMesh *r_weld_mesh) | ||||
| BVHTreeOverlap *overlap, | |||||
| const uint overlap_len, | |||||
| WeldMesh *r_weld_mesh) | |||||
| { | { | ||||
| const MEdge *medge = mesh->medge; | const MEdge *medge = mesh->medge; | ||||
| const MLoop *mloop = mesh->mloop; | const MLoop *mloop = mesh->mloop; | ||||
| Context not available. | |||||
| const uint mloop_len = mesh->totloop; | const uint mloop_len = mesh->totloop; | ||||
| const uint mpoly_len = mesh->totpoly; | const uint mpoly_len = mesh->totpoly; | ||||
| uint *vert_dest_map = MEM_mallocN(sizeof(*vert_dest_map) * mvert_len, __func__); | uint *vert_dest_map = r_weld_mesh->vert_groups_map; | ||||
| uint *edge_dest_map = MEM_mallocN(sizeof(*edge_dest_map) * medge_len, __func__); | uint *edge_dest_map = MEM_mallocN(sizeof(*edge_dest_map) * medge_len, __func__); | ||||
| struct WeldGroup *v_links = MEM_callocN(sizeof(*v_links) * mvert_len, __func__); | struct WeldGroup *v_links = MEM_callocN(sizeof(*v_links) * mvert_len, __func__); | ||||
| WeldVert *wvert; | WeldVert *wvert; | ||||
| uint wvert_len; | uint wvert_len; | ||||
| weld_vert_ctx_alloc_and_setup(mvert_len, | weld_vert_ctx_alloc_and_setup(mvert_len, vert_dest_map, &wvert, &wvert_len); | ||||
| overlap, | |||||
| overlap_len, | |||||
| vert_dest_map, | |||||
| &wvert, | |||||
| &wvert_len, | |||||
| &r_weld_mesh->vert_kill_len); | |||||
| uint *edge_ctx_map; | uint *edge_ctx_map; | ||||
| WeldEdge *wedge; | WeldEdge *wedge; | ||||
| Context not available. | |||||
| &r_weld_mesh->edge_groups_buffer, | &r_weld_mesh->edge_groups_buffer, | ||||
| &r_weld_mesh->edge_groups); | &r_weld_mesh->edge_groups); | ||||
Not Done Inline ActionsWhy was this line removed? mano-wii: Why was this line removed? | |||||
Done Inline ActionsAs weld_mesh should be setup in weld_mesh_context_create I removed vert_groups_map from it and made it an argument to the functions that need it. The problem is that this free will not be called if no vertices where merged, but the memory would still have been allocated. So I wanted both cases in one and put it at the bottom of doWeld. I think of vert_dest_map as the backbone of the whole modifier, because it is used all throughout. It should not be part of weld_mesh, which is only used for one (conditional) step of the modifier. weasel: As `weld_mesh` should be setup in `weld_mesh_context_create` I removed `vert_groups_map` from… | |||||
| r_weld_mesh->vert_groups_map = vert_dest_map; | |||||
| r_weld_mesh->edge_groups_map = edge_dest_map; | r_weld_mesh->edge_groups_map = edge_dest_map; | ||||
| MEM_freeN(v_links); | MEM_freeN(v_links); | ||||
| MEM_freeN(wvert); | MEM_freeN(wvert); | ||||
| Context not available. | |||||
| * \{ */ | * \{ */ | ||||
| struct WeldOverlapData { | struct WeldOverlapData { | ||||
| const MVert *mvert; | MVert *mvert; | ||||
| float merge_dist_sq; | float merge_dist_sq; | ||||
| }; | }; | ||||
| static bool bvhtree_weld_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) | static bool bvhtree_weld_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) | ||||
| Context not available. | |||||
| BLI_bitmap *v_mask = NULL; | BLI_bitmap *v_mask = NULL; | ||||
| int v_mask_act = 0; | int v_mask_act = 0; | ||||
| const MVert *mvert; | MVert *mvert, *mv; | ||||
| const MLoop *mloop; | MLoop *mloop, *ml; | ||||
| const MPoly *mpoly, *mp; | MEdge *medge, *me; | ||||
| MPoly *mpoly, *mp; | |||||
| uint totvert, totedge, totloop, totpoly; | uint totvert, totedge, totloop, totpoly; | ||||
| uint i; | |||||
| mvert = mesh->mvert; | mvert = mesh->mvert; | ||||
| medge = mesh->medge; | |||||
| mloop = mesh->mloop; | |||||
| mpoly = mesh->mpoly; | |||||
| totvert = mesh->totvert; | totvert = mesh->totvert; | ||||
| totedge = mesh->totedge; | |||||
| totloop = mesh->totloop; | |||||
| totpoly = mesh->totpoly; | |||||
| if (wmd->merge_dist == 0.0f) { | |||||
| return mesh; | |||||
| } | |||||
| /* Vertex Group. */ | /* Vertex Group. */ | ||||
| const int defgrp_index = BKE_object_defgroup_name_index(ob, wmd->defgrp_name); | const int defgrp_index = BKE_object_defgroup_name_index(ob, wmd->defgrp_name); | ||||
| Context not available. | |||||
| const bool invert_vgroup = (wmd->flag & MOD_WELD_INVERT_VGROUP) != 0; | const bool invert_vgroup = (wmd->flag & MOD_WELD_INVERT_VGROUP) != 0; | ||||
| dv = &dvert[0]; | dv = &dvert[0]; | ||||
| v_mask = BLI_BITMAP_NEW(totvert, __func__); | v_mask = BLI_BITMAP_NEW(totvert, __func__); | ||||
| for (i = 0; i < totvert; i++, dv++) { | for (uint i = 0; i < totvert; i++, dv++) { | ||||
| const bool found = BKE_defvert_find_weight(dv, defgrp_index) > 0.0f; | const bool found = BKE_defvert_find_weight(dv, defgrp_index) > 0.0f; | ||||
| if (found != invert_vgroup) { | if (found != invert_vgroup) { | ||||
| BLI_BITMAP_ENABLE(v_mask, i); | BLI_BITMAP_ENABLE(v_mask, i); | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| /* Get overlap map. */ | if (v_mask && v_mask_act == 0) { | ||||
| /* TODO: For a better performanse use KD-Tree. */ | |||||
| struct BVHTreeFromMesh treedata; | |||||
| BVHTree *bvhtree = bvhtree_from_mesh_verts_ex(&treedata, | |||||
| mvert, | |||||
| totvert, | |||||
| false, | |||||
| v_mask, | |||||
| v_mask_act, | |||||
| wmd->merge_dist / 2, | |||||
| 2, | |||||
| 6, | |||||
| 0, | |||||
| NULL, | |||||
| NULL); | |||||
| if (v_mask) { | |||||
| MEM_freeN(v_mask); | MEM_freeN(v_mask); | ||||
| } | return mesh; | ||||
| if (bvhtree == NULL) { | |||||
| return result; | |||||
| } | } | ||||
| struct WeldOverlapData data; | struct WeldOverlapData data; | ||||
| data.mvert = mvert; | data.mvert = MEM_malloc_arrayN(totvert, sizeof(*mvert), __func__); | ||||
| memcpy(data.mvert, mvert, totvert * sizeof(*mvert)); | |||||
| data.merge_dist_sq = square_f(wmd->merge_dist); | data.merge_dist_sq = square_f(wmd->merge_dist); | ||||
| uint overlap_len; | BLI_bitmap *e_boundary = NULL; | ||||
| BVHTreeOverlap *overlap = BLI_bvhtree_overlap_ex(bvhtree, | if ((wmd->mode == MOD_WELD_FULL_MODE && (wmd->flag & MOD_WELD_FULL_ONLY_BOUNDARY)) || | ||||
| bvhtree, | (wmd->mode == MOD_WELD_SIMPLE_MODE && (wmd->flag & MOD_WELD_SIMPLE_DO_BOUNDARY))) { | ||||
| &overlap_len, | /* Quickly find boundary. */ | ||||
| bvhtree_weld_overlap_cb, | e_boundary = BLI_BITMAP_NEW(totedge, __func__); | ||||
| &data, | BLI_bitmap *e_has_more_polys = BLI_BITMAP_NEW(totedge, __func__); | ||||
| wmd->max_interactions, | ml = &mloop[0]; | ||||
| BVH_OVERLAP_RETURN_PAIRS); | for (uint i = 0; i < totloop; i++, ml++) { | ||||
| uint e = ml->e; | |||||
| if (v_mask_act > 0 && | |||||
| (!BLI_BITMAP_TEST(v_mask, medge[e].v1) || !BLI_BITMAP_TEST(v_mask, medge[e].v2))) { | |||||
| continue; | |||||
| } | |||||
| BLI_bitmap *enable = BLI_BITMAP_TEST(e_boundary, ml->e) ? e_has_more_polys : e_boundary; | |||||
| BLI_BITMAP_ENABLE(enable, ml->e); | |||||
| } | |||||
| BLI_bitmap_flip_all(e_has_more_polys, totedge); | |||||
| BLI_bitmap_and_all(e_boundary, e_has_more_polys, totedge); | |||||
| MEM_freeN(e_has_more_polys); | |||||
| } | |||||
| uint vert_kill_len = 0; | |||||
| /* Vertex destination map. (here short as vm). */ | |||||
| uint *vm = MEM_malloc_arrayN(totvert, sizeof(*mvert), __func__); | |||||
| for (uint i = 0; i < totvert; i++) { | |||||
| vm[i] = i; | |||||
| } | |||||
| uint *combined_verts = MEM_calloc_arrayN(totvert, sizeof(*combined_verts), __func__); | |||||
| float edgedir[3]; | |||||
| if (wmd->mode == MOD_WELD_FULL_MODE) { | |||||
| if (wmd->flag & MOD_WELD_FULL_ONLY_BOUNDARY) { | |||||
| v_mask_act = 0; | |||||
| BLI_bitmap *rim_mask = BLI_BITMAP_NEW(totvert, __func__); | |||||
| uint num_blocks = _BITMAP_NUM_BLOCKS(totedge); | |||||
| for (uint i = 0; i < num_blocks; i++) { | |||||
| BLI_bitmap section = e_boundary[i]; | |||||
| while (section) { | |||||
| int index = ffs(section) - 1; | |||||
| section &= (section - 1); | |||||
| uint edge = i * (sizeof(BLI_bitmap) * 8) + index; | |||||
| if (LIKELY(edge < totedge)) { | |||||
| uint v1 = medge[edge].v1; | |||||
| if (!BLI_BITMAP_TEST(rim_mask, v1)) { | |||||
| BLI_BITMAP_ENABLE(rim_mask, v1); | |||||
| if (!v_mask || BLI_BITMAP_TEST(v_mask, v1)) { | |||||
| v_mask_act++; | |||||
| } | |||||
| } | |||||
| uint v2 = medge[edge].v2; | |||||
| if (!BLI_BITMAP_TEST(rim_mask, v2)) { | |||||
| BLI_BITMAP_ENABLE(rim_mask, v2); | |||||
| if (!v_mask || BLI_BITMAP_TEST(v_mask, v2)) { | |||||
| v_mask_act++; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (v_mask) { | |||||
| BLI_bitmap_and_all(v_mask, rim_mask, totvert); | |||||
| MEM_freeN(rim_mask); | |||||
| } | |||||
| else { | |||||
| v_mask = rim_mask; | |||||
| } | |||||
| } | |||||
| /* Get overlap map. */ | |||||
| /* TODO: For a better performanse use KD-Tree. */ | |||||
| struct BVHTreeFromMesh treedata; | |||||
| BVHTree *bvhtree = bvhtree_from_mesh_verts_ex(&treedata, | |||||
| mvert, | |||||
| totvert, | |||||
| false, | |||||
| v_mask, | |||||
| v_mask_act, | |||||
| wmd->merge_dist / 2, | |||||
| 2, | |||||
| 6, | |||||
| 0, | |||||
| NULL, | |||||
| NULL); | |||||
| if (bvhtree == NULL) { | |||||
| MEM_freeN(vm); | |||||
| MEM_freeN(combined_verts); | |||||
| if (v_mask) { | |||||
| MEM_freeN(v_mask); | |||||
| } | |||||
| if (e_boundary) { | |||||
| MEM_freeN(e_boundary); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| uint overlap_len; | |||||
| BVHTreeOverlap *overlap = BLI_bvhtree_overlap_ex(bvhtree, | |||||
| bvhtree, | |||||
| &overlap_len, | |||||
| bvhtree_weld_overlap_cb, | |||||
| &data, | |||||
| wmd->max_interactions, | |||||
| BVH_OVERLAP_RETURN_PAIRS); | |||||
| free_bvhtree_from_mesh(&treedata); | |||||
| /* TODO: move vertex position calculation here to have better merge groups. */ | |||||
| const BVHTreeOverlap *overlap_iter = &overlap[0]; | |||||
| for (uint i = 0; i < overlap_len; i++, overlap_iter++) { | |||||
| uint indexA = overlap_iter->indexA; | |||||
| uint indexB = overlap_iter->indexB; | |||||
| BLI_assert(indexA < indexB); | |||||
| uint va_dst = vm[indexA]; | |||||
| while (va_dst != vm[va_dst]) { | |||||
| va_dst = vm[va_dst]; | |||||
| } | |||||
| uint vb_dst = vm[indexB]; | |||||
| while (vb_dst != vm[vb_dst]) { | |||||
| vb_dst = vm[vb_dst]; | |||||
| } | |||||
| if (va_dst == vb_dst) { | |||||
| continue; | |||||
| } | |||||
| if (va_dst > vb_dst) { | |||||
| SWAP(uint, va_dst, vb_dst); | |||||
| } | |||||
| vert_kill_len++; | |||||
| vm[vb_dst] = va_dst; | |||||
| } | |||||
| #ifdef USE_WELD_DEBUG | |||||
| weld_assert_vert_dest_map_setup(overlap, overlap_len, vm); | |||||
| #endif | |||||
| MEM_freeN(overlap); | |||||
| } | |||||
| if (wmd->mode == MOD_WELD_SIMPLE_MODE && wmd->flag & MOD_WELD_SIMPLE_DO_BOUNDARY) { | |||||
| uint *vert_adj_edges_len = MEM_calloc_arrayN(totvert, sizeof(*vert_adj_edges_len), __func__); | |||||
| /* iterate through rim edges */ | |||||
| uint rim_edges_len = 0; | |||||
| uint num_blocks = _BITMAP_NUM_BLOCKS(totedge); | |||||
| for (uint i = 0; i < num_blocks; i++) { | |||||
| BLI_bitmap section = e_boundary[i]; | |||||
| while (section) { | |||||
| int index = ffs(section) - 1; | |||||
| section &= (section - 1); | |||||
| uint edge = i * (sizeof(BLI_bitmap) * 8) + index; | |||||
| if (LIKELY(edge < totedge)) { | |||||
| rim_edges_len++; | |||||
| vert_adj_edges_len[medge[edge].v1]++; | |||||
| vert_adj_edges_len[medge[edge].v2]++; | |||||
| } | |||||
| } | |||||
| } | |||||
| free_bvhtree_from_mesh(&treedata); | if (rim_edges_len) { | ||||
| uint **vert_adj_edges = MEM_calloc_arrayN(totvert, sizeof(*vert_adj_edges), __func__); | |||||
Done Inline ActionsHere instead of a comment, you can use a BLI_assert(MOD_WELD_ALL_MODE). mano-wii: Here instead of a comment, you can use a `BLI_assert(MOD_WELD_ALL_MODE)`.
Thus, in addition to… | |||||
| me = medge; | |||||
| for (uint i = 0; i < num_blocks; i++) { | |||||
| BLI_bitmap section = e_boundary[i]; | |||||
| while (section) { | |||||
| int index = ffs(section) - 1; | |||||
| section &= (section - 1); | |||||
| uint edge = i * (sizeof(BLI_bitmap) * 8) + index; | |||||
| if (LIKELY(edge < totedge)) { | |||||
| const uint *vs = (uint *)&medge[edge]; | |||||
| for (uint j = 0; j < 2; j++) { | |||||
| const uint vert = vs[j]; | |||||
| const uint len = vert_adj_edges_len[vert]; | |||||
| if (len > 0) { | |||||
| uint *adj_edges = vert_adj_edges[vert]; | |||||
| if (adj_edges == NULL) { | |||||
| adj_edges = MEM_malloc_arrayN(len, sizeof(*adj_edges), __func__); | |||||
| adj_edges[0] = edge; | |||||
| for (uint k = 1; k < len; k++) { | |||||
| adj_edges[k] = OUT_OF_CONTEXT; | |||||
| } | |||||
| vert_adj_edges[vert] = adj_edges; | |||||
| } | |||||
| else { | |||||
| uint *ed = adj_edges; | |||||
| for (uint k = 0; k < len && *ed != OUT_OF_CONTEXT; k++, ed++) { | |||||
| /* Pass. */ | |||||
| } | |||||
| BLI_assert(*ed == OUT_OF_CONTEXT); | |||||
| *ed = edge; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| BLI_bitmap *e_used = BLI_BITMAP_NEW(totvert, __func__); | |||||
| if (overlap_len) { | for (uint i = 0; i < totvert; i++) { | ||||
| if (vert_adj_edges_len[i] > 0 && i == vm[i]) { | |||||
| if (BLI_BITMAP_TEST(e_used, i)) { | |||||
| continue; | |||||
| } | |||||
| uint v1 = i; | |||||
| uint v2 = OUT_OF_CONTEXT; | |||||
| float edgedir2[3]; | |||||
| float min_distance_sq = data.merge_dist_sq; | |||||
| for (uint j = 0; j < totvert; j++) { | |||||
| uint new_v2 = j; | |||||
| while (new_v2 != vm[new_v2]) { | |||||
| new_v2 = vm[new_v2]; | |||||
| } | |||||
| if (vert_adj_edges_len[new_v2] > 0) { | |||||
| if (new_v2 != v1) { | |||||
| sub_v3_v3v3(edgedir2, data.mvert[new_v2].co, data.mvert[v1].co); | |||||
| const float dist_sq = len_squared_v3(edgedir2); | |||||
| if (dist_sq < min_distance_sq) { | |||||
| min_distance_sq = dist_sq; | |||||
| v2 = new_v2; | |||||
| copy_v3_v3(edgedir, edgedir2); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (v2 == OUT_OF_CONTEXT) { | |||||
| continue; | |||||
| } | |||||
| if (v1 == v2) { | |||||
| continue; | |||||
| } | |||||
| while (v2 != OUT_OF_CONTEXT) { | |||||
| vert_kill_len++; | |||||
| mul_v3_fl(edgedir, | |||||
| (combined_verts[v2] + 1) / | |||||
| (float)(combined_verts[v1] + combined_verts[v2] + 2)); | |||||
| add_v3_v3(data.mvert[v1].co, edgedir); | |||||
| if (v1 > v2) { | |||||
| combined_verts[v2] += combined_verts[v1] + 1; | |||||
| vm[v1] = v2; | |||||
| } | |||||
| else { | |||||
| combined_verts[v1] += combined_verts[v2] + 1; | |||||
| vm[v2] = v1; | |||||
| } | |||||
| BLI_BITMAP_ENABLE(e_used, v1); | |||||
| BLI_BITMAP_ENABLE(e_used, v2); | |||||
| uint len1 = vert_adj_edges_len[v1]; | |||||
| uint len2 = vert_adj_edges_len[v2]; | |||||
| uint *adj_edges1 = vert_adj_edges[v1]; | |||||
| uint *adj_edges2 = vert_adj_edges[v2]; | |||||
| uint merge_v1 = OUT_OF_CONTEXT; | |||||
| uint merge_v2 = OUT_OF_CONTEXT; | |||||
| min_distance_sq = data.merge_dist_sq; | |||||
| for (uint j = 0; j < len1; j++) { | |||||
| uint new_v1 = ((uint *)&medge[adj_edges1[j]])[medge[adj_edges1[j]].v1 == v1]; | |||||
| if (new_v1 != vm[new_v1]) { | |||||
| continue; | |||||
| } | |||||
| for (uint k = 0; k < len2; k++) { | |||||
| uint new_v2 = ((uint *)&medge[adj_edges2[k]])[medge[adj_edges2[k]].v1 == v2]; | |||||
| if (new_v2 != vm[new_v2] || BLI_BITMAP_TEST(e_used, new_v2)) { | |||||
| continue; | |||||
| } | |||||
| /* Only consider merging if not merged yet. */ | |||||
| if (new_v2 != new_v1) { | |||||
| sub_v3_v3v3(edgedir2, data.mvert[new_v2].co, data.mvert[new_v1].co); | |||||
| const float dist_sq = len_squared_v3(edgedir2); | |||||
| if (dist_sq < min_distance_sq) { | |||||
| min_distance_sq = dist_sq; | |||||
| merge_v1 = new_v1; | |||||
| merge_v2 = new_v2; | |||||
| copy_v3_v3(edgedir, edgedir2); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| v1 = merge_v1; | |||||
| v2 = merge_v2; | |||||
| } | |||||
| } | |||||
| } | |||||
| for (int i = 0; i < totvert; i++) { | |||||
| if (vert_adj_edges_len[i] > 0) { | |||||
| MEM_freeN(vert_adj_edges[i]); | |||||
| } | |||||
| } | |||||
| MEM_freeN(vert_adj_edges); | |||||
| MEM_freeN(e_used); | |||||
| } | |||||
| MEM_freeN(vert_adj_edges_len); | |||||
| } | |||||
| if (wmd->mode == MOD_WELD_SIMPLE_MODE && wmd->flag & MOD_WELD_SIMPLE_DO_INSIDE) { | |||||
| BLI_bitmap *e_mask = BLI_BITMAP_NEW(totedge, __func__); | |||||
| me = &medge[0]; | |||||
| for (uint i = 0; i < totedge; i++, me++) { | |||||
| if (!BLI_BITMAP_TEST(e_mask, i)) { | |||||
| uint v1 = vm[me->v1]; | |||||
| while (v1 != vm[v1]) { | |||||
| v1 = vm[v1]; | |||||
| } | |||||
| uint v2 = vm[me->v2]; | |||||
| while (v2 != vm[v2]) { | |||||
| v2 = vm[v2]; | |||||
| } | |||||
| if (v1 == v2) { | |||||
| continue; | |||||
| } | |||||
| if (v_mask_act > 0 && (!BLI_BITMAP_TEST(v_mask, v1) || !BLI_BITMAP_TEST(v_mask, v2))) { | |||||
| continue; | |||||
| } | |||||
| if (v1 > v2) { | |||||
| SWAP(uint, v1, v2); | |||||
| } | |||||
| sub_v3_v3v3(edgedir, data.mvert[v2].co, data.mvert[v1].co); | |||||
| const float dist_sq = len_squared_v3(edgedir); | |||||
| if (dist_sq <= data.merge_dist_sq) { | |||||
| vert_kill_len++; | |||||
| BLI_BITMAP_ENABLE(e_mask, i); | |||||
| mul_v3_fl(edgedir, | |||||
| (combined_verts[v2] + 1) / | |||||
| (float)(combined_verts[v1] + combined_verts[v2] + 2)); | |||||
| add_v3_v3(data.mvert[v1].co, edgedir); | |||||
| combined_verts[v1] += combined_verts[v2] + 1; | |||||
| vm[v2] = v1; | |||||
| } | |||||
| } | |||||
| } | |||||
| MEM_freeN(e_mask); | |||||
| } | |||||
| MEM_freeN(combined_verts); | |||||
| if (v_mask) { | |||||
| MEM_freeN(v_mask); | |||||
| } | |||||
| if (e_boundary) { | |||||
| MEM_freeN(e_boundary); | |||||
| } | |||||
| if (vert_kill_len) { | |||||
| WeldMesh weld_mesh; | WeldMesh weld_mesh; | ||||
| weld_mesh_context_create(mesh, overlap, overlap_len, &weld_mesh); | weld_mesh.vert_groups_map = vm; | ||||
| weld_mesh.vert_kill_len = vert_kill_len; | |||||
| weld_mesh_context_create(mesh, &weld_mesh); | |||||
| mloop = mesh->mloop; | mloop = mesh->mloop; | ||||
| mpoly = mesh->mpoly; | mpoly = mesh->mpoly; | ||||
| Context not available. | |||||
| uint *vert_final = weld_mesh.vert_groups_map; | uint *vert_final = weld_mesh.vert_groups_map; | ||||
| uint *index_iter = &vert_final[0]; | uint *index_iter = &vert_final[0]; | ||||
| int dest_index = 0; | int dest_index = 0; | ||||
| for (i = 0; i < totvert; i++, index_iter++) { | for (uint i = 0; i < totvert; i++, index_iter++) { | ||||
| int source_index = i; | int source_index = i; | ||||
| int count = 0; | int count = 0; | ||||
| while (i < totvert && *index_iter == OUT_OF_CONTEXT) { | while (i < totvert && *index_iter == OUT_OF_CONTEXT) { | ||||
Done Inline ActionsIt will probably be necessary to create a versioning (in ...\source\blender\blenloader\intern\versioning_290.c) to adapt old files. mano-wii: It will probably be necessary to create a versioning (in `... | |||||
Done Inline ActionsI dont think so. I made sure current "Full" Mode is mode=0 so when old files get loaded it will automatically choose Full (AFAIK and tested) weasel: I dont think so. I made sure current "Full" Mode is mode=0 so when old files get loaded it will… | |||||
| Context not available. | |||||
| uint *edge_final = weld_mesh.edge_groups_map; | uint *edge_final = weld_mesh.edge_groups_map; | ||||
| index_iter = &edge_final[0]; | index_iter = &edge_final[0]; | ||||
| dest_index = 0; | dest_index = 0; | ||||
| for (i = 0; i < totedge; i++, index_iter++) { | for (uint i = 0; i < totedge; i++, index_iter++) { | ||||
| int source_index = i; | int source_index = i; | ||||
| int count = 0; | int count = 0; | ||||
| while (i < totedge && *index_iter == OUT_OF_CONTEXT) { | while (i < totedge && *index_iter == OUT_OF_CONTEXT) { | ||||
| Context not available. | |||||
| } | } | ||||
| if (count) { | if (count) { | ||||
| CustomData_copy_data(&mesh->edata, &result->edata, source_index, dest_index, count); | CustomData_copy_data(&mesh->edata, &result->edata, source_index, dest_index, count); | ||||
| MEdge *me = &result->medge[dest_index]; | me = &result->medge[dest_index]; | ||||
| dest_index += count; | dest_index += count; | ||||
| for (; count--; me++) { | for (; count--; me++) { | ||||
| me->v1 = vert_final[me->v1]; | me->v1 = vert_final[me->v1]; | ||||
| Context not available. | |||||
| &weld_mesh.edge_groups_buffer[wegrp->group.ofs], | &weld_mesh.edge_groups_buffer[wegrp->group.ofs], | ||||
| wegrp->group.len, | wegrp->group.len, | ||||
| dest_index); | dest_index); | ||||
| MEdge *me = &result->medge[dest_index]; | me = &result->medge[dest_index]; | ||||
| me->v1 = vert_final[wegrp->v1]; | me->v1 = vert_final[wegrp->v1]; | ||||
| me->v2 = vert_final[wegrp->v2]; | me->v2 = vert_final[wegrp->v2]; | ||||
| me->flag |= ME_LOOSEEDGE; | me->flag |= ME_LOOSEEDGE; | ||||
| Context not available. | |||||
| uint r_i = 0; | uint r_i = 0; | ||||
| int loop_cur = 0; | int loop_cur = 0; | ||||
| uint *group_buffer = BLI_array_alloca(group_buffer, weld_mesh.max_poly_len); | uint *group_buffer = BLI_array_alloca(group_buffer, weld_mesh.max_poly_len); | ||||
| for (i = 0; i < totpoly; i++, mp++) { | for (uint i = 0; i < totpoly; i++, mp++) { | ||||
| int loop_start = loop_cur; | int loop_start = loop_cur; | ||||
| uint poly_ctx = weld_mesh.poly_map[i]; | uint poly_ctx = weld_mesh.poly_map[i]; | ||||
| if (poly_ctx == OUT_OF_CONTEXT) { | if (poly_ctx == OUT_OF_CONTEXT) { | ||||
| Context not available. | |||||
| } | } | ||||
| WeldPoly *wp = &weld_mesh.wpoly_new[0]; | WeldPoly *wp = &weld_mesh.wpoly_new[0]; | ||||
| for (i = 0; i < weld_mesh.wpoly_new_len; i++, wp++) { | for (uint i = 0; i < weld_mesh.wpoly_new_len; i++, wp++) { | ||||
| int loop_start = loop_cur; | int loop_start = loop_cur; | ||||
| WeldLoopOfPolyIter iter; | WeldLoopOfPolyIter iter; | ||||
| if (!weld_iter_loop_of_poly_begin( | if (!weld_iter_loop_of_poly_begin( | ||||
| Context not available. | |||||
| weld_mesh_context_free(&weld_mesh); | weld_mesh_context_free(&weld_mesh); | ||||
| } | } | ||||
| else { | |||||
| MEM_freeN(vm); | |||||
| } | |||||
| MEM_freeN(overlap); | MEM_freeN(data.mvert); | ||||
| return result; | return result; | ||||
| } | } | ||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Weld Modifier Main | |||||
| * \{ */ | |||||
| static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) | static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) | ||||
| { | { | ||||
| WeldModifierData *wmd = (WeldModifierData *)md; | WeldModifierData *wmd = (WeldModifierData *)md; | ||||
| return weldModifier_doWeld(wmd, ctx, mesh); | return weldModifier_doWeld(wmd, ctx, mesh); | ||||
| } | } | ||||
| Context not available. | |||||
| wmd->merge_dist = 0.001f; | wmd->merge_dist = 0.001f; | ||||
| wmd->max_interactions = 1; | wmd->max_interactions = 1; | ||||
| wmd->mode = MOD_WELD_SIMPLE_MODE; | |||||
| wmd->flag = MOD_WELD_SIMPLE_DO_BOUNDARY | MOD_WELD_SIMPLE_DO_INSIDE; | |||||
| wmd->defgrp_name[0] = '\0'; | wmd->defgrp_name[0] = '\0'; | ||||
| } | } | ||||
| Context not available. | |||||
| PointerRNA ob_ptr; | PointerRNA ob_ptr; | ||||
| PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); | PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); | ||||
| int mode = RNA_enum_get(ptr, "mode"); | |||||
| uiLayoutSetPropSep(layout, true); | uiLayoutSetPropSep(layout, true); | ||||
| uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); | |||||
| uiItemR(layout, ptr, "merge_threshold", 0, IFACE_("Distance"), ICON_NONE); | uiItemR(layout, ptr, "merge_threshold", 0, IFACE_("Distance"), ICON_NONE); | ||||
| uiItemR(layout, ptr, "max_interactions", 0, NULL, ICON_NONE); | |||||
| if (mode == MOD_WELD_FULL_MODE) { | |||||
| uiItemR(layout, ptr, "max_interactions", 0, NULL, ICON_NONE); | |||||
| uiItemR(layout, ptr, "only_boundary", 0, NULL, ICON_NONE); | |||||
| } | |||||
| else if (mode == MOD_WELD_SIMPLE_MODE) { | |||||
| uiItemR(layout, ptr, "do_inside", 0, NULL, ICON_NONE); | |||||
| uiItemR(layout, ptr, "do_boundary", 0, NULL, ICON_NONE); | |||||
| } | |||||
| modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); | modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); | ||||
| modifier_panel_end(layout, ptr); | modifier_panel_end(layout, ptr); | ||||
| Context not available. | |||||
I'm not sure if "weld_fix_vert_map" is an appropriate name
A comment here would be useful to explain what this function fixes.