diff options
-rw-r--r-- | source/blender/blenkernel/intern/editderivedmesh.c | 46 | ||||
-rw-r--r-- | source/blender/bmesh/bmesh_class.h | 7 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_core.c | 2 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh.c | 249 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh.h | 2 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh_conv.c | 1 | ||||
-rw-r--r-- | source/blender/bmesh/tools/bmesh_decimate_collapse.c | 2 | ||||
-rw-r--r-- | source/blender/bmesh/tools/bmesh_edgenet.c | 2 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_array.c | 2 | ||||
-rw-r--r-- | source/blender/python/bmesh/bmesh_py_types.c | 12 |
10 files changed, 305 insertions, 20 deletions
diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index 152540041f7..5862c5358c4 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -173,7 +173,26 @@ static void emDM_calcNormals(DerivedMesh *dm) static void emDM_calcLoopNormals(DerivedMesh *dm, const float split_angle) { - /* Do nothing for now! */ + EditDerivedBMesh *bmdm = (EditDerivedBMesh *)dm; + BMesh *bm = bmdm->em->bm; + const float (*vertexCos)[3], (*vertexNos)[3], (*polyNos)[3]; + float (*loopNos)[3]; + + /* calculate loop normals from poly and vertex normals */ + emDM_ensureVertNormals(bmdm); + dm->dirty &= ~DM_DIRTY_NORMALS; + + vertexCos = bmdm->vertexCos; + vertexNos = bmdm->vertexNos; + polyNos = bmdm->polyNos; + + loopNos = dm->getLoopDataArray(dm, CD_NORMAL); + if (!loopNos) { + DM_add_loop_layer(dm, CD_NORMAL, CD_CALLOC, NULL); + loopNos = dm->getLoopDataArray(dm, CD_NORMAL); + } + + BM_loops_calc_normal_vcos(bm, vertexCos, vertexNos, polyNos, split_angle, loopNos); } static void emDM_recalcTessellation(DerivedMesh *UNUSED(dm)) @@ -1530,6 +1549,31 @@ static void *emDM_getTessFaceDataArray(DerivedMesh *dm, int type) } } + /* Special handling for CD_TESSLOOPNORMAL, we generate it on demand as well. */ + if (type == CD_TESSLOOPNORMAL) { + const float (*lnors)[3] = dm->getLoopDataArray(dm, CD_NORMAL); + + if (lnors) { + BMLoop *(*looptris)[3] = bmdm->em->looptris; + short (*tlnors)[4][3], (*tlnor)[4][3]; + int index, i, j; + + DM_add_tessface_layer(dm, type, CD_CALLOC, NULL); + index = CustomData_get_layer_index(&dm->faceData, type); + dm->faceData.layers[index].flag |= CD_FLAG_TEMPORARY; + + tlnor = tlnors = DM_get_tessface_data_layer(dm, type); + + BM_mesh_elem_index_ensure(bm, BM_LOOP); + + for (i = 0; i < bmdm->em->tottri; i++, tlnor++, looptris++) { + for (j = 0; j < 3; j++) { + normal_float_to_short_v3((*tlnor)[j], lnors[BM_elem_index_get((*looptris)[j])]); + } + } + } + } + return datalayer; } diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 01e4c911d19..ea7505bcba4 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -66,7 +66,7 @@ typedef struct BMHeader { int index; /* notes: * - Use BM_elem_index_get/set macros for index * - Uninitialized to -1 so we can easily tell its not set. - * - Used for edge/vert/face, check BMesh.elem_index_dirty for valid index values, + * - Used for edge/vert/face/loop, check BMesh.elem_index_dirty for valid index values, * this is abused by various tools which set it dirty. * - For loops this is used for sorting during tessellation. */ @@ -188,9 +188,8 @@ typedef struct BMesh { int totvertsel, totedgesel, totfacesel; /* flag index arrays as being dirty so we can check if they are clean and - * avoid looping over the entire vert/edge/face array in those cases. - * valid flags are - BM_VERT | BM_EDGE | BM_FACE. - * BM_LOOP isn't handled so far. */ + * avoid looping over the entire vert/edge/face/loop array in those cases. + * valid flags are - BM_VERT | BM_EDGE | BM_FACE | BM_LOOP. */ char elem_index_dirty; /* flag array table as being dirty so we know when its safe to use it, diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index bf19be1a965..a06921b87fe 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -211,6 +211,8 @@ static BMLoop *bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, l->prev = NULL; /* --- done --- */ + /* may add to middle of the pool */ + bm->elem_index_dirty |= BM_LOOP; bm->totloop++; diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index 23572d3ba16..e8dbfa09929 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -31,6 +31,7 @@ #include "DNA_listBase.h" #include "DNA_object_types.h" +#include "BLI_linklist_stack.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -431,6 +432,230 @@ void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (* MEM_freeN(edgevec); } +/** + * Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normals_vnos + */ +static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float split_angle, + float (*r_lnos)[3]) +{ + BMIter eiter; + BMEdge *e; + int i; + + const bool check_angle = (split_angle < (float)M_PI); + + if (check_angle) { + split_angle = cosf(split_angle); + } + + { + char hflag = BM_LOOP; + if (vnos) + hflag |= BM_VERT; + if (fnos) + hflag |= BM_FACE; + BM_mesh_elem_index_ensure(bm, hflag); + } + + /* This first loop checks which edges are actually smooth, and pre-populate lnos with vnos (as if they were + * all smooth). + */ + BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) { + BMLoop *l_a, *l_b; + + BM_elem_index_set(e, i); /* set_inline */ + BM_elem_flag_disable(e, BM_ELEM_TAG); /* Clear tag (means edge is sharp). */ + + /* An edge with only two loops, might be smooth... */ + if (BM_edge_loop_pair(e, &l_a, &l_b)) { + bool is_angle_smooth = true; + if (check_angle) { + const float *no_a = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_a->f->no; + const float *no_b = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_b->f->no; + is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle); + } + + /* We only tag edges that are *really* smooth... */ + if (is_angle_smooth && + BM_elem_flag_test_bool(e, BM_ELEM_SMOOTH) && + BM_elem_flag_test_bool(l_a->f, BM_ELEM_SMOOTH) && + BM_elem_flag_test_bool(l_b->f, BM_ELEM_SMOOTH)) + { + const float *no; + BM_elem_flag_enable(e, BM_ELEM_TAG); + + /* linked vertices might be fully smooth, copy their normals to loop ones. */ + no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no; + copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no); + no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no; + copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no); + } + } + } + + bm->elem_index_dirty &= ~BM_EDGE; +} + +/* BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c */ +static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3]) +{ + BMIter fiter; + BMFace *f_curr; + + /* Temp normal stack. */ + BLI_SMALLSTACK_DECLARE(normal, float *); + + { + char hflag = BM_LOOP; + if (vcos) + hflag |= BM_VERT; + if (fnos) + hflag |= BM_FACE; + BM_mesh_elem_index_ensure(bm, hflag); + } + + /* We now know edges that can be smoothed (they are tagged), and edges that will be hard (they aren't). + * Now, time to generate the normals. + */ + BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) { + BMLoop *l_curr, *l_first; + + l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr); + do { + if (BM_elem_flag_test_bool(l_curr->e, BM_ELEM_TAG)) { + /* A smooth edge. + * We skip it because it is either: + * - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit + * one of its ends, i.e. one of its two sharp edges), or... + * - the related vertex is a "full smooth" one, in which case pre-populated normals from vertex + * are just fine! + */ + } + else if (!BM_elem_flag_test_bool(l_curr->prev->e, BM_ELEM_TAG)) { + /* Simple case (both edges around that vertex are sharp in related polygon), + * this vertex just takes its poly normal. + */ + const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no; + copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no); + } + else { + /* We have to fan around current vertex, until we find the other non-smooth edge, + * and accumulate face normals into the vertex! + * Note in case this vertex has only one sharp edge, this is a waste because the normal is the same as + * the vertex normal, but I do not see any easy way to detect that (would need to count number + * of sharp edges per vertex, I doubt the additional memory usage would be worth it, especially as + * it should not be a common case in real-life meshes anyway). + */ + BMVert *v_pivot = l_curr->v; + BMEdge *e_next; + BMLoop *lfan_pivot, *lfan_pivot_next; + float lnor[3] = {0.0f, 0.0f, 0.0f}; + float vec_curr[3], vec_next[3]; + + const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; + + lfan_pivot = l_curr; + e_next = lfan_pivot->e; /* Current edge here, actually! */ + + /* Only need to compute previous edge's vector once, then we can just reuse old current one! */ + { + const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot); + const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; + + sub_v3_v3v3(vec_curr, co_2, co_pivot); + normalize_v3(vec_curr); + } + + while (true) { + /* Much simpler than in sibling code with basic Mesh data! */ + lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next); + BLI_assert(lfan_pivot_next->v == v_pivot); + + /* Compute edge vector. + * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing them + * twice (or more) here. However, time gained is not worth memory and time lost, + * given the fact that this code should not be called that much in real-life meshes... + */ + { + const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot); + const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; + + sub_v3_v3v3(vec_next, co_2, co_pivot); + normalize_v3(vec_next); + } + + { + /* Code similar to accumulate_vertex_normals_poly. */ + /* Calculate angle between the two poly edges incident on this vertex. */ + const BMFace *f = lfan_pivot->f; + const float fac = saacos(dot_v3v3(vec_next, vec_curr)); + const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no; + /* Accumulate */ + madd_v3_v3fl(lnor, no, fac); + } + + /* We store here a pointer to all loop-normals processed. */ + BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[BM_elem_index_get(lfan_pivot)]); + + if (!BM_elem_flag_test_bool(e_next, BM_ELEM_TAG)) { + /* Next edge is sharp, we have finished with this fan of faces around this vert! */ + break; + } + + /* Copy next edge vector to current one. */ + copy_v3_v3(vec_curr, vec_next); + /* Next pivot loop to current one. */ + lfan_pivot = lfan_pivot_next; + } + + /* In case we get a zero normal here, just use vertex normal already set! */ + if (LIKELY(normalize_v3(lnor) != 0.0f)) { + /* Copy back the final computed normal into all related loop-normals. */ + float *nor; + while ((nor = BLI_SMALLSTACK_POP(normal))) { + copy_v3_v3(nor, lnor); + } + } + } + } while ((l_curr = l_curr->next) != l_first); + } + + BLI_SMALLSTACK_FREE(normal); +} + +#if 0 /* Unused currently */ +/** + * \brief BMesh Compute Loop Normals + * + * Updates the loop normals of a mesh. Assumes vertex and face normals are valid (else call BM_mesh_normals_update() + * first)! + */ +void BM_mesh_loop_normals_update(BMesh *bm, const float split_angle, float (*r_lnos)[3]) +{ + /* Tag smooth edges and set lnos from vnos when they might be completely smooth... */ + bm_mesh_edges_sharp_tag(bm, NULL, NULL, split_angle, r_lnos); + + /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ + bm_mesh_loops_calc_normals(bm, NULL, NULL, r_lnos); +} +#endif + +/** + * \brief BMesh Compute Loop Normals from/to external data. + * + * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals'). + * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry (splitting edges). + */ +void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], + const float split_angle, float (*r_lnos)[3]) +{ + /* Tag smooth edges and set lnos from vnos when they might be completely smooth... */ + bm_mesh_edges_sharp_tag(bm, vnos, fnos, split_angle, r_lnos); + + /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ + bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos); +} + static void UNUSED_FUNCTION(bm_mdisps_space_set)(Object *ob, BMesh *bm, int from, int to) { /* switch multires data out of tangent space */ @@ -585,19 +810,35 @@ void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag) #pragma omp section { - if (hflag & BM_FACE) { - if (bm->elem_index_dirty & BM_FACE) { + if (hflag & (BM_FACE | BM_LOOP)) { + if (bm->elem_index_dirty & (BM_FACE | BM_LOOP)) { BMIter iter; BMElem *ele; + const bool hflag_loop = (hflag & BM_LOOP) && (bm->elem_index_dirty & BM_LOOP); + int index; + int index_loop_start = 0; BM_ITER_MESH_INDEX (ele, &iter, bm, BM_FACES_OF_MESH, index) { BM_elem_index_set(ele, index); /* set_ok */ + + if (hflag_loop) { + BMIter liter; + BMElem *lele; + + int index_diff; + BM_ITER_ELEM_INDEX (lele, &liter, ele, BM_LOOPS_OF_FACE, index_diff) { + BM_elem_index_set(lele, index_loop_start + index_diff); /* set_ok */ + } + index_loop_start += index_diff; + } } BLI_assert(index == bm->totface); + if (hflag & BM_LOOP) + BLI_assert(index_loop_start == bm->totloop); } else { - // printf("%s: skipping face index calc!\n", __func__); + // printf("%s: skipping face/loop index calc!\n", __func__); } } } @@ -1006,7 +1247,7 @@ void BM_mesh_remap(BMesh *bm, unsigned int *vert_idx, unsigned int *edge_idx, un BLI_ghash_insert(fptr_map, (void *)*fap, (void *)new_fap); } - bm->elem_index_dirty |= BM_FACE; + bm->elem_index_dirty |= BM_FACE | BM_LOOP; MEM_freeN(faces_pool); MEM_freeN(faces_copy); diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index c3ff30e5a2b..3923c2515a3 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -39,6 +39,8 @@ void BM_mesh_clear(BMesh *bm); void BM_mesh_normals_update(BMesh *bm); void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*vcos)[3], float (*vnos)[3]); +void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*pnos)[3], + const float split_angle, float (*r_lnos)[3]); void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag); void bmesh_edit_end(BMesh *bm, const BMOpTypeFlag type_flag); diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c index 775c917e4aa..e9c00053930 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.c +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c @@ -441,6 +441,7 @@ void BM_mesh_bm_from_me(BMesh *bm, Mesh *me, } bm->elem_index_dirty &= ~BM_FACE; /* added in order, clear dirty flag */ + bm->elem_index_dirty |= BM_LOOP; /* did not set the loop indices */ if (me->mselect && me->totselect != 0) { diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index 00915d60428..ef628a10607 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -1006,7 +1006,7 @@ void BM_mesh_decimate_collapse(BMesh *bm, const float factor, float *vweights, c bm_decim_build_edge_cost(bm, vquadrics, vweights, eheap, eheap_table); face_tot_target = bm->totface * factor; - bm->elem_index_dirty |= BM_FACE | BM_EDGE | BM_VERT; + bm->elem_index_dirty |= BM_FACE | BM_LOOP | BM_EDGE | BM_VERT; #ifdef USE_CUSTOMDATA diff --git a/source/blender/bmesh/tools/bmesh_edgenet.c b/source/blender/bmesh/tools/bmesh_edgenet.c index 55df5cbc955..ddf43949258 100644 --- a/source/blender/bmesh/tools/bmesh_edgenet.c +++ b/source/blender/bmesh/tools/bmesh_edgenet.c @@ -500,7 +500,7 @@ void BM_mesh_edgenet(BMesh *bm, BLI_assert(BLI_mempool_count(path_pool) == 0); } - bm->elem_index_dirty |= BM_FACE; + bm->elem_index_dirty |= BM_FACE | BM_LOOP; BLI_mempool_destroy(edge_queue_pool); BLI_mempool_destroy(path_pool); diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 694b66b5076..c2fa9a5e8b5 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -191,7 +191,7 @@ static int *find_doubles_index_map(BMesh *bm, BMOperator *dupe_op, } /* above loops over all, so set all to dirty, if this is somehow * setting valid values, this line can be removed - campbell */ - bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_index_dirty |= BM_ALL; (*index_map_length) = i; index_map = MEM_callocN(sizeof(int) * (*index_map_length), "index_map"); diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 6302561cb7e..c2b496f914b 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -176,9 +176,7 @@ static int bpy_bm_elem_index_set(BPy_BMElem *self, PyObject *value, void *UNUSED BM_elem_index_set(self->ele, param); /* set_dirty! */ /* when setting the index assume its set invalid */ - if (self->ele->head.htype & (BM_VERT | BM_EDGE | BM_FACE)) { - self->bm->elem_index_dirty |= self->ele->head.htype; - } + self->bm->elem_index_dirty |= self->ele->head.htype; return 0; } @@ -2264,11 +2262,9 @@ static PyObject *bpy_bmelemseq_index_update(BPy_BMElemSeq *self) index++; } - if (htype & (BM_VERT | BM_EDGE | BM_FACE)) { - /* since this isn't the normal vert/edge/face loops, - * we're setting dirty values here. so tag as dirty. */ - bm->elem_index_dirty |= htype; - } + /* since this isn't the normal vert/edge/face loops, + * we're setting dirty values here. so tag as dirty. */ + bm->elem_index_dirty |= htype; break; } |