diff options
author | Hans Goudey <h.goudey@me.com> | 2022-01-24 06:55:39 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-01-24 06:55:39 +0300 |
commit | df1ef9d32ae96db93367115d3f7c496a22748b97 (patch) | |
tree | 9bb39142f77b44b6e7278d4d411f1d81939eafe9 /source/blender | |
parent | ae1649e905701420afe7cf1566fdafa438b3472a (diff) | |
parent | c69a581c0b951d219f1501a8ceb7040bdf36e51c (diff) |
Merge branch 'master' into temp-geometry-nodes-extrude-mesh
Diffstat (limited to 'source/blender')
18 files changed, 558 insertions, 298 deletions
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index f57d4da4d26..e2ed005cf9e 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -98,7 +98,7 @@ set(SRC intern/bmesh_marking.h intern/bmesh_mesh.c intern/bmesh_mesh.h - intern/bmesh_mesh_convert.c + intern/bmesh_mesh_convert.cc intern/bmesh_mesh_convert.h intern/bmesh_mesh_debug.c intern/bmesh_mesh_debug.h diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.c b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index a7dd5be5cbd..0b2bdb03dd2 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.c +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -79,8 +79,11 @@ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" +#include "BLI_array.hh" +#include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_span.hh" #include "BKE_customdata.h" #include "BKE_mesh.h" @@ -95,6 +98,10 @@ #include "bmesh.h" #include "intern/bmesh_private.h" /* For element checking. */ +using blender::Array; +using blender::IndexRange; +using blender::Span; + void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag) { const char cd_flag_all = BM_mesh_cd_flag_from_bmesh(bm) | cd_flag; @@ -107,9 +114,9 @@ void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag) void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag) { /* CustomData_bmesh_init_pool() must run first */ - BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != NULL); - BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != NULL); - BLI_assert(bm->pdata.totlayer == 0 || bm->pdata.pool != NULL); + BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != nullptr); + BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != nullptr); + BLI_assert(bm->pdata.totlayer == 0 || bm->pdata.pool != nullptr); if (cd_flag & ME_CDFLAG_VERT_BWEIGHT) { if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { @@ -175,35 +182,28 @@ char BM_mesh_cd_flag_from_bmesh(BMesh *bm) } /* Static function for alloc (duplicate in modifiers_bmesh.c) */ -static BMFace *bm_face_create_from_mpoly( - MPoly *mp, MLoop *ml, BMesh *bm, BMVert **vtable, BMEdge **etable) +static BMFace *bm_face_create_from_mpoly(BMesh &bm, + Span<MLoop> loops, + Span<BMVert *> vtable, + Span<BMEdge *> etable) { - BMVert **verts = BLI_array_alloca(verts, mp->totloop); - BMEdge **edges = BLI_array_alloca(edges, mp->totloop); - int j; + Array<BMVert *, BM_DEFAULT_NGON_STACK_SIZE> verts(loops.size()); + Array<BMEdge *, BM_DEFAULT_NGON_STACK_SIZE> edges(loops.size()); - for (j = 0; j < mp->totloop; j++, ml++) { - verts[j] = vtable[ml->v]; - edges[j] = etable[ml->e]; + for (const int i : loops.index_range()) { + verts[i] = vtable[loops[i].v]; + edges[i] = etable[loops[i].e]; } - return BM_face_create(bm, verts, edges, mp->totloop, NULL, BM_CREATE_SKIP_CD); + return BM_face_create(&bm, verts.data(), edges.data(), loops.size(), nullptr, BM_CREATE_SKIP_CD); } void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshParams *params) { const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer || bm->pdata.totlayer || bm->ldata.totlayer)); - MVert *mvert; - MEdge *medge; - MLoop *mloop; - MPoly *mp; - KeyBlock *actkey, *block; - BMVert *v, **vtable = NULL; - BMEdge *e, **etable = NULL; - BMFace *f, **ftable = NULL; - float(*keyco)[3] = NULL; - int totloops, i; + KeyBlock *actkey; + float(*keyco)[3] = nullptr; CustomData_MeshMasks mask = CD_MASK_BMESH; CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); @@ -225,7 +225,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Only copy normals to the new BMesh if they are not already dirty. This avoids unnecessary * work, but also accessing normals on an incomplete mesh, for example when restoring undo steps * in edit mode. */ - const float(*vert_normals)[3] = NULL; + const float(*vert_normals)[3] = nullptr; if (!BKE_mesh_vertex_normals_are_dirty(me)) { vert_normals = BKE_mesh_vertex_normals_ensure(me); } @@ -246,7 +246,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* -------------------------------------------------------------------- */ /* Shape Key */ int tot_shape_keys = 0; - if (me->key != NULL && DEG_is_original_id(&me->id)) { + if (me->key != nullptr && DEG_is_original_id(&me->id)) { /* Evaluated meshes can be topologically inconsistent with their shape keys. * Shape keys are also already integrated into the state of the evaluated * mesh, so considering them here would kind of apply them twice. */ @@ -273,20 +273,20 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar if (is_new == false) { tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)); } - const float(**shape_key_table)[3] = tot_shape_keys ? - BLI_array_alloca(shape_key_table, tot_shape_keys) : - NULL; + const float(**shape_key_table)[3] = tot_shape_keys ? (const float(**)[3])BLI_array_alloca( + shape_key_table, tot_shape_keys) : + nullptr; if ((params->active_shapekey != 0) && tot_shape_keys > 0) { - actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1); + actkey = static_cast<KeyBlock *>(BLI_findlink(&me->key->block, params->active_shapekey - 1)); } else { - actkey = NULL; + actkey = nullptr; } if (is_new) { if (tot_shape_keys || params->add_key_index) { - CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, nullptr, 0); } } @@ -301,26 +301,29 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar __func__); me->key->uidgen = 1; - for (block = me->key->block.first; block; block = block->next) { + LISTBASE_FOREACH (KeyBlock *, block, &me->key->block) { block->uid = me->key->uidgen++; } } } if (actkey && actkey->totelem == me->totvert) { - keyco = params->use_shapekey ? actkey->data : NULL; + keyco = params->use_shapekey ? static_cast<float(*)[3]>(actkey->data) : nullptr; if (is_new) { bm->shapenr = params->active_shapekey; } } - for (i = 0, block = me->key->block.first; i < tot_shape_keys; block = block->next, i++) { + int i; + KeyBlock *block; + for (i = 0, block = static_cast<KeyBlock *>(me->key->block.first); i < tot_shape_keys; + block = block->next, i++) { if (is_new) { - CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, CD_ASSIGN, NULL, 0, block->name); + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, CD_ASSIGN, nullptr, 0, block->name); int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); bm->vdata.layers[j].uid = block->uid; } - shape_key_table[i] = (const float(*)[3])block->data; + shape_key_table[i] = static_cast<const float(*)[3]>(block->data); } } @@ -349,17 +352,18 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; - vtable = MEM_mallocN(sizeof(BMVert **) * me->totvert, __func__); - - for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { - v = vtable[i] = BM_vert_create(bm, keyco ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD); + Span<MVert> mvert{me->mvert, me->totvert}; + Array<BMVert *> vtable(me->totvert); + for (const int i : mvert.index_range()) { + BMVert *v = vtable[i] = BM_vert_create( + bm, keyco ? keyco[i] : mvert[i].co, nullptr, BM_CREATE_SKIP_CD); BM_elem_index_set(v, i); /* set_ok */ /* Transfer flag. */ - v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT); + v->head.hflag = BM_vert_flag_from_mflag(mvert[i].flag & ~SELECT); /* This is necessary for selection counts to work properly. */ - if (mvert->flag & SELECT) { + if (mvert[i].flag & SELECT) { BM_vert_select_set(bm, v, true); } @@ -371,7 +375,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true); if (cd_vert_bweight_offset != -1) { - BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert->bweight / 255.0f); + BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert[i].bweight / 255.0f); } /* Set shape key original index. */ @@ -381,7 +385,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Set shape-key data. */ if (tot_shape_keys) { - float(*co_dst)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); + float(*co_dst)[3] = (float(*)[3])BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); for (int j = 0; j < tot_shape_keys; j++, co_dst++) { copy_v3_v3(*co_dst, shape_key_table[j][i]); } @@ -391,19 +395,18 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar bm->elem_index_dirty &= ~BM_VERT; /* Added in order, clear dirty flag. */ } - etable = MEM_mallocN(sizeof(BMEdge **) * me->totedge, __func__); - - medge = me->medge; - for (i = 0; i < me->totedge; i++, medge++) { - e = etable[i] = BM_edge_create( - bm, vtable[medge->v1], vtable[medge->v2], NULL, BM_CREATE_SKIP_CD); + Span<MEdge> medge{me->medge, me->totedge}; + Array<BMEdge *> etable(me->totedge); + for (const int i : medge.index_range()) { + BMEdge *e = etable[i] = BM_edge_create( + bm, vtable[medge[i].v1], vtable[medge[i].v2], nullptr, BM_CREATE_SKIP_CD); BM_elem_index_set(e, i); /* set_ok */ /* Transfer flags. */ - e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT); + e->head.hflag = BM_edge_flag_from_mflag(medge[i].flag & ~SELECT); /* This is necessary for selection counts to work properly. */ - if (medge->flag & SELECT) { + if (medge[i].flag & SELECT) { BM_edge_select_set(bm, e, true); } @@ -411,33 +414,35 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true); if (cd_edge_bweight_offset != -1) { - BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge->bweight / 255.0f); + BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge[i].bweight / 255.0f); } if (cd_edge_crease_offset != -1) { - BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)medge->crease / 255.0f); + BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)medge[i].crease / 255.0f); } } if (is_new) { bm->elem_index_dirty &= ~BM_EDGE; /* Added in order, clear dirty flag. */ } + Span<MPoly> mpoly{me->mpoly, me->totpoly}; + Span<MLoop> mloop{me->mloop, me->totloop}; + /* Only needed for selection. */ + + Array<BMFace *> ftable; if (me->mselect && me->totselect != 0) { - ftable = MEM_mallocN(sizeof(BMFace **) * me->totpoly, __func__); + ftable.reinitialize(me->totpoly); } - mloop = me->mloop; - mp = me->mpoly; - for (i = 0, totloops = 0; i < me->totpoly; i++, mp++) { - BMLoop *l_iter; - BMLoop *l_first; - - f = bm_face_create_from_mpoly(mp, mloop + mp->loopstart, bm, vtable, etable); - if (ftable != NULL) { + int totloops = 0; + for (const int i : mpoly.index_range()) { + BMFace *f = bm_face_create_from_mpoly( + *bm, mloop.slice(mpoly[i].loopstart, mpoly[i].totloop), vtable, etable); + if (!ftable.is_empty()) { ftable[i] = f; } - if (UNLIKELY(f == NULL)) { + if (UNLIKELY(f == nullptr)) { printf( "%s: Warning! Bad face in mesh" " \"%s\" at index %d!, skipping\n", @@ -451,20 +456,21 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar BM_elem_index_set(f, bm->totface - 1); /* set_ok */ /* Transfer flag. */ - f->head.hflag = BM_face_flag_from_mflag(mp->flag & ~ME_FACE_SEL); + f->head.hflag = BM_face_flag_from_mflag(mpoly[i].flag & ~ME_FACE_SEL); /* This is necessary for selection counts to work properly. */ - if (mp->flag & ME_FACE_SEL) { + if (mpoly[i].flag & ME_FACE_SEL) { BM_face_select_set(bm, f, true); } - f->mat_nr = mp->mat_nr; + f->mat_nr = mpoly[i].mat_nr; if (i == me->act_face) { bm->act_face = f; } - int j = mp->loopstart; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); + int j = mpoly[i].loopstart; + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; do { /* Don't use 'j' since we may have skipped some faces, hence some loops. */ BM_elem_index_set(l_iter, totloops++); /* set_ok */ @@ -485,44 +491,39 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar } /* -------------------------------------------------------------------- */ - /* MSelect clears the array elements (avoid adding multiple times). + /* MSelect clears the array elements (to avoid adding multiple times). * * Take care to keep this last and not use (v/e/ftable) after this. */ if (me->mselect && me->totselect != 0) { - MSelect *msel; - for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) { + for (const int i : IndexRange(me->totselect)) { + const MSelect &msel = me->mselect[i]; + BMElem **ele_p; - switch (msel->type) { + switch (msel.type) { case ME_VSEL: - ele_p = (BMElem **)&vtable[msel->index]; + ele_p = (BMElem **)&vtable[msel.index]; break; case ME_ESEL: - ele_p = (BMElem **)&etable[msel->index]; + ele_p = (BMElem **)&etable[msel.index]; break; case ME_FSEL: - ele_p = (BMElem **)&ftable[msel->index]; + ele_p = (BMElem **)&ftable[msel.index]; break; default: continue; } - if (*ele_p != NULL) { + if (*ele_p != nullptr) { BM_select_history_store_notest(bm, *ele_p); - *ele_p = NULL; + *ele_p = nullptr; } } } else { BM_select_history_clear(bm); } - - MEM_freeN(vtable); - MEM_freeN(etable); - if (ftable) { - MEM_freeN(ftable); - } } /** @@ -531,7 +532,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) { const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); - BMVert **vertMap = NULL; + BMVert **vertMap = nullptr; BMVert *eve; int i = 0; BMIter iter; @@ -539,7 +540,7 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) /* Caller needs to ensure this. */ BLI_assert(ototvert > 0); - vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap"); + vertMap = static_cast<BMVert **>(MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap")); if (cd_shape_keyindex_offset != -1) { BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); @@ -547,7 +548,7 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) /* Not fool-proof, but chances are if we have many verts with the same index, * we will want to use the first one, * since the second is more likely to be a duplicate. */ - (vertMap[keyi] == NULL)) { + (vertMap[keyi] == nullptr)) { vertMap[keyi] = eve; } } @@ -617,7 +618,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); - MVert *oldverts = NULL; + MVert *oldverts = nullptr; const int ototvert = me->totvert; if (me->key && (cd_shape_keyindex_offset != -1)) { @@ -628,9 +629,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh oldverts = MEM_dupallocN(me->mvert); #else oldverts = me->mvert; - me->mvert = NULL; + me->mvert = nullptr; CustomData_update_typemap(&me->vdata); - CustomData_set_layer(&me->vdata, CD_MVERT, NULL); + CustomData_set_layer(&me->vdata, CD_MVERT, nullptr); #endif } @@ -647,7 +648,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh me->totloop = bm->totloop; me->totpoly = bm->totface; /* Will be overwritten with a valid value if 'dotess' is set, otherwise we - * end up with 'me->totface' and me->mface == NULL which can crash T28625. */ + * end up with 'me->totface' and me->mface == nullptr which can crash T28625. */ me->totface = 0; me->act_face = -1; @@ -660,10 +661,14 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly); } - MVert *mvert = bm->totvert ? MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") : NULL; - MEdge *medge = bm->totedge ? MEM_callocN(sizeof(MEdge) * bm->totedge, "bm_to_me.edge") : NULL; - MLoop *mloop = bm->totloop ? MEM_callocN(sizeof(MLoop) * bm->totloop, "bm_to_me.loop") : NULL; - MPoly *mpoly = bm->totface ? MEM_callocN(sizeof(MPoly) * bm->totface, "bm_to_me.poly") : NULL; + MVert *mvert = bm->totvert ? (MVert *)MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") : + nullptr; + MEdge *medge = bm->totedge ? (MEdge *)MEM_callocN(sizeof(MEdge) * bm->totedge, "bm_to_me.edge") : + nullptr; + MLoop *mloop = bm->totloop ? (MLoop *)MEM_callocN(sizeof(MLoop) * bm->totloop, "bm_to_me.loop") : + nullptr; + MPoly *mpoly = bm->totface ? (MPoly *)MEM_callocN(sizeof(MPoly) * bm->totface, "bm_to_me.poly") : + nullptr; CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); @@ -677,7 +682,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm); /* This is called again, 'dotess' arg is used there. */ - BKE_mesh_update_customdata_pointers(me, 0); + BKE_mesh_update_customdata_pointers(me, false); i = 0; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { @@ -767,15 +772,13 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh /* Patch hook indices and vertex parents. */ if (params->calc_object_remap && (ototvert > 0)) { - BLI_assert(bmain != NULL); - Object *ob; - ModifierData *md; - BMVert **vertMap = NULL; + BLI_assert(bmain != nullptr); + BMVert **vertMap = nullptr; - for (ob = bmain->objects.first; ob; ob = ob->id.next) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { if ((ob->parent) && (ob->parent->data == me) && ELEM(ob->partype, PARVERT1, PARVERT3)) { - if (vertMap == NULL) { + if (vertMap == nullptr) { vertMap = bm_to_mesh_vertex_map(bm, ototvert); } @@ -799,11 +802,11 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } if (ob->data == me) { - for (md = ob->modifiers.first; md; md = md->next) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->type == eModifierType_Hook) { HookModifierData *hmd = (HookModifierData *)md; - if (vertMap == NULL) { + if (vertMap == nullptr) { vertMap = bm_to_mesh_vertex_map(bm, ototvert); } @@ -834,15 +837,15 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh BKE_mesh_update_customdata_pointers(me, false); { - BMEditSelection *selected; me->totselect = BLI_listbase_count(&(bm->selected)); MEM_SAFE_FREE(me->mselect); if (me->totselect != 0) { - me->mselect = MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + me->mselect = static_cast<MSelect *>( + MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history")); } - for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { + LISTBASE_FOREACH_INDEX (BMEditSelection *, selected, &bm->selected, i) { if (selected->htype == BM_VERT) { me->mselect[i].type = ME_VSEL; } @@ -861,9 +864,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh if (me->key) { KeyBlock *currkey; - KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1); + KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&me->key->block, bm->shapenr - 1)); - float(*ofs)[3] = NULL; + float(*ofs)[3] = nullptr; /* Go through and find any shape-key custom-data layers * that might not have corresponding KeyBlocks, and add them if necessary. */ @@ -872,7 +875,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh continue; } - for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + for (currkey = (KeyBlock *)me->key->block.first; currkey; currkey = currkey->next) { if (currkey->uid == bm->vdata.layers[i].uid) { break; } @@ -890,10 +893,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh /* Unlikely, but the active key may not be valid if the * BMesh and the mesh are out of sync. */ - (actkey != NULL) && + (actkey != nullptr) && /* Not used here, but 'oldverts' is used later for applying 'ofs'. */ - (oldverts != NULL) && + (oldverts != nullptr) && /* Needed for referencing oldverts. */ (cd_shape_keyindex_offset != -1)) { @@ -902,9 +905,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh /* Active key is a base. */ if (act_is_basis) { - const float(*fp)[3] = actkey->data; + const float(*fp)[3] = static_cast<const float(*)[3]>(actkey->data); - ofs = MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data"); + ofs = static_cast<float(*)[3]>( + MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data")); mvert = me->mvert; BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); @@ -918,7 +922,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh * because it will only work for the existing vertices and not the new * ones, creating a mess when doing e.g. subdivide + translate. */ MEM_freeN(ofs); - ofs = NULL; + ofs = nullptr; break; } @@ -927,7 +931,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } - for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + LISTBASE_FOREACH (KeyBlock *, currkey, &me->key->block) { int keyi; const float(*ofs_pt)[3] = ofs; float *newkey, (*oldkey)[3], *fp; @@ -937,11 +941,12 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, currkey_uuid); - const bool apply_offset = (cd_shape_offset != -1) && (ofs != NULL) && (currkey != actkey) && - (bm->shapenr - 1 == currkey->relative); + const bool apply_offset = (cd_shape_offset != -1) && (ofs != nullptr) && + (currkey != actkey) && (bm->shapenr - 1 == currkey->relative); - fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"); - oldkey = currkey->data; + fp = newkey = static_cast<float *>( + MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data")); + oldkey = static_cast<float(*)[3]>(currkey->data); mvert = me->mvert; BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { @@ -962,9 +967,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } else if (cd_shape_offset != -1) { /* In most cases this runs. */ - copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); + copy_v3_v3(fp, (const float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); } - else if ((oldkey != NULL) && (cd_shape_keyindex_offset != -1) && + else if ((oldkey != nullptr) && (cd_shape_keyindex_offset != -1) && ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && (keyi < currkey->totelem)) { /* Old method of reconstructing keys via vertices original key indices, @@ -984,7 +989,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh * Otherwise, in case we call again #BM_mesh_bm_to_me on same BMesh, * we'll apply diff from previous call to #BM_mesh_bm_to_me, * to shape-key values from *original creation of the BMesh*. See T50524. */ - copy_v3_v3(BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp); + copy_v3_v3((float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp); } fp += 3; @@ -1014,7 +1019,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } - if (oldverts != NULL) { + if (oldverts != nullptr) { MEM_freeN(oldverts); } @@ -1029,7 +1034,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * { /* Must be an empty mesh. */ BLI_assert(me->totvert == 0); - BLI_assert(cd_mask_extra == NULL || (cd_mask_extra->vmask & CD_MASK_SHAPEKEY) == 0); + BLI_assert(cd_mask_extra == nullptr || (cd_mask_extra->vmask & CD_MASK_SHAPEKEY) == 0); me->totvert = bm->totvert; me->totedge = bm->totedge; @@ -1037,19 +1042,19 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * me->totloop = bm->totloop; me->totpoly = bm->totface; - CustomData_add_layer(&me->vdata, CD_ORIGINDEX, CD_CALLOC, NULL, bm->totvert); - CustomData_add_layer(&me->edata, CD_ORIGINDEX, CD_CALLOC, NULL, bm->totedge); - CustomData_add_layer(&me->pdata, CD_ORIGINDEX, CD_CALLOC, NULL, bm->totface); + CustomData_add_layer(&me->vdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totvert); + CustomData_add_layer(&me->edata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totedge); + CustomData_add_layer(&me->pdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totface); - CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, bm->totvert); - CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, NULL, bm->totedge); - CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, bm->totloop); - CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, bm->totface); + CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, bm->totvert); + CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, nullptr, bm->totedge); + CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, nullptr, bm->totloop); + CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, nullptr, bm->totface); /* Don't process shape-keys, we only feed them through the modifier stack as needed, * e.g. for applying modifiers or the like. */ CustomData_MeshMasks mask = CD_MASK_DERIVEDMESH; - if (cd_mask_extra != NULL) { + if (cd_mask_extra != nullptr) { CustomData_MeshMasks_update(&mask, cd_mask_extra); } mask.vmask &= ~CD_MASK_SHAPEKEY; @@ -1082,7 +1087,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * /* Don't add origindex layer if one already exists. */ add_orig = !CustomData_has_layer(&bm->pdata, CD_ORIGINDEX); - index = CustomData_get_layer(&me->vdata, CD_ORIGINDEX); + index = (int *)CustomData_get_layer(&me->vdata, CD_ORIGINDEX); BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { MVert *mv = &mvert[i]; @@ -1105,7 +1110,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } bm->elem_index_dirty &= ~BM_VERT; - index = CustomData_get_layer(&me->edata, CD_ORIGINDEX); + index = (int *)CustomData_get_layer(&me->edata, CD_ORIGINDEX); BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) { MEdge *med = &medge[i]; @@ -1138,7 +1143,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } bm->elem_index_dirty &= ~BM_EDGE; - index = CustomData_get_layer(&me->pdata, CD_ORIGINDEX); + index = (int *)CustomData_get_layer(&me->pdata, CD_ORIGINDEX); j = 0; BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { BMLoop *l_iter; diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h index 977142578ca..4b3c4cbff82 100644 --- a/source/blender/bmesh/intern/bmesh_private.h +++ b/source/blender/bmesh/intern/bmesh_private.h @@ -27,6 +27,10 @@ * parts of the bmesh internals. */ +#ifdef __cplusplus +extern "C" { +#endif + /* returns positive nonzero on error */ #ifdef NDEBUG @@ -102,3 +106,7 @@ void poly_rotate_plane(const float normal[3], float (*verts)[3], uint nverts); /* include the rest of our private declarations */ #include "bmesh_structure.h" + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 5d573271ea3..59c357aa416 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -687,19 +687,15 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key * .active_shapekey = um->shapenr, })); + em_tmp = BKE_editmesh_create(bm); + *em = *em_tmp; + /* Normals should not be stored in the undo mesh, so recalculate them. The edit * mesh is expected to have valid normals and there is no tracked dirty state. */ BLI_assert(BKE_mesh_vertex_normals_are_dirty(&um->me)); - BM_mesh_normals_update(bm); - - em_tmp = BKE_editmesh_create(bm); - *em = *em_tmp; /* Calculate face normals and tessellation at once since it's multi-threaded. */ - BKE_editmesh_looptri_calc_ex(em, - &(const struct BMeshCalcTessellation_Params){ - .face_normals = true, - }); + BKE_editmesh_looptri_and_normals_calc(em); em->selectmode = um->selectmode; bm->selectmode = um->selectmode; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 59937bb0757..6a3a27a6630 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -409,6 +409,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE); uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); + uiItemR(col, ptr, "use_back_face_culling", 0, NULL, ICON_NONE); } static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -580,6 +581,7 @@ static void face_mark_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "use_face_mark_invert", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "use_face_mark_boundaries", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_face_mark_keep_contour", 0, NULL, ICON_NONE); } static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -607,6 +609,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_fuzzy_all", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_loose_edge_chain", 0, IFACE_("Loose Edges"), ICON_NONE); uiItemR(col, ptr, "use_loose_as_contour", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_detail_preserve", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_geometry_space_chain", 0, IFACE_("Geometry Space"), ICON_NONE); uiItemR(layout, diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 8c5820c9667..cb17d1d5f1a 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -300,14 +300,18 @@ typedef struct LineartRenderBuffer { bool use_loose_edge_chain; bool use_geometry_space_chain; bool use_image_boundary_trimming; + bool use_back_face_culling; bool filter_face_mark; bool filter_face_mark_invert; bool filter_face_mark_boundaries; + bool filter_face_mark_keep_contour; bool force_crease; bool sharp_as_crease; + bool chain_preserve_details; + /* Keep an copy of these data so when line art is running it's self-contained. */ bool cam_is_persp; float cam_obmat[4][4]; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 0deb8b1c335..78ad895c93e 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -571,6 +571,57 @@ static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartEdg } } +static bool lineart_chain_fix_ambiguous_segments(LineartEdgeChain *ec, + LineartEdgeChainItem *last_matching_eci, + float distance_threshold, + bool preserve_details, + LineartEdgeChainItem **r_next_eci) +{ + float dist_accum = 0; + + int fixed_occ = last_matching_eci->occlusion; + unsigned char fixed_mask = last_matching_eci->material_mask_bits; + + LineartEdgeChainItem *can_skip_to = NULL; + LineartEdgeChainItem *last_eci = last_matching_eci; + for (LineartEdgeChainItem *eci = last_matching_eci->next; eci; eci = eci->next) { + dist_accum += len_v2v2(last_eci->pos, eci->pos); + if (dist_accum > distance_threshold) { + break; + } + last_eci = eci; + /* The reason for this is because we don't want visible segments to be "skipped" into + * connecting with invisible segments. */ + if (eci->occlusion < fixed_occ) { + break; + } + if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ) { + can_skip_to = eci; + } + } + if (can_skip_to) { + /* Either mark all in-between segments with the same occlusion and mask or delete those + * different ones. */ + LineartEdgeChainItem *next_eci; + for (LineartEdgeChainItem *eci = last_matching_eci->next; eci != can_skip_to; eci = next_eci) { + next_eci = eci->next; + if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ) { + continue; + } + if (preserve_details) { + eci->material_mask_bits = fixed_mask; + eci->occlusion = fixed_occ; + } + else { + BLI_remlink(&ec->chain, eci); + } + } + *r_next_eci = can_skip_to; + return true; + } + return false; +} + void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) { LineartEdgeChain *ec, *new_ec; @@ -597,6 +648,13 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) if (lineart_point_overlapping(next_eci, eci->pos[0], eci->pos[1], 1e-5)) { continue; } + if (lineart_chain_fix_ambiguous_segments(ec, + eci->prev, + rb->chaining_image_threshold, + rb->chain_preserve_details, + &next_eci)) { + continue; + } } else { /* Set the same occlusion level for the end vertex, so when further connection is needed diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 5461b80cad1..cb57ce3f940 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1346,6 +1346,10 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) /* Select the triangle in the array. */ tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i); + if (tri->flags & LRT_CULL_DISCARD) { + continue; + } + LRT_CULL_DECIDE_INSIDE LRT_CULL_ENSURE_MEMORY lineart_triangle_cull_single(rb, @@ -1489,6 +1493,7 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, FreestyleEdge *fel, *fer; bool face_mark_filtered = false; uint16_t edge_flag_result = 0; + bool only_contour = false; if (use_freestyle_face && rb->filter_face_mark) { fel = CustomData_bmesh_get(&bm_if_freestyle->pdata, ll->f->head.data, CD_FREESTYLE_FACE); @@ -1513,7 +1518,12 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, face_mark_filtered = !face_mark_filtered; } if (!face_mark_filtered) { - return 0; + if (rb->filter_face_mark_keep_contour) { + only_contour = true; + } + else { + return 0; + } } } @@ -1536,8 +1546,31 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, double dot_1 = 0, dot_2 = 0; double result; - if (rb->cam_is_persp) { - sub_v3_v3v3_db(view_vector, l->gloc, rb->camera_pos); + if (rb->use_contour || rb->use_back_face_culling) { + + if (rb->cam_is_persp) { + sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc); + } + else { + view_vector = rb->view_vector; + } + + dot_1 = dot_v3v3_db(view_vector, tri1->gn); + dot_2 = dot_v3v3_db(view_vector, tri2->gn); + + if (rb->use_contour && (result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { + edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; + } + + /* Because the ray points towards the camera, so backface is when dot value being negative.*/ + if (rb->use_back_face_culling) { + if (dot_1 < 0) { + tri1->flags |= LRT_CULL_DISCARD; + } + if (dot_2 < 0) { + tri2->flags |= LRT_CULL_DISCARD; + } + } } else { view_vector = rb->view_vector; @@ -1550,6 +1583,12 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } + /* For when face mark filtering decided that we discard the face but keep_contour option is on. + * so we still have correct full contour around the object. */ + if (only_contour) { + return edge_flag_result; + } + if (rb->use_crease) { if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; @@ -1875,7 +1914,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu bm); if (eflag) { /* Only allocate for feature lines (instead of all lines) to save memory. - * If allow duplicated edges, one edge gets added multiple times if it has multiple types. */ + * If allow duplicated edges, one edge gets added multiple times if it has multiple types. + */ allocate_la_e += rb->allow_duplicated_types ? lineart_edge_type_duplication_count(eflag) : 1; } /* Here we just use bm's flag for when loading actual lines, then we don't need to call @@ -2069,8 +2109,8 @@ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], } bool cond[6] = {true, true, true, true, true, true}; - /* Because for a point to be inside clip space, it must satisfy `-Wc <= XYCc <= Wc`, here if all - * verts falls to the same side of the clip space border, we know it's outside view. */ + /* Because for a point to be inside clip space, it must satisfy `-Wc <= XYCc <= Wc`, here if + * all verts falls to the same side of the clip space border, we know it's outside view. */ for (int i = 0; i < 8; i++) { cond[0] &= (co[i][0] < -co[i][3]); cond[1] &= (co[i][0] > co[i][3]); @@ -2145,7 +2185,8 @@ static void lineart_main_load_geometries( int thread_count = rb->thread_count; - /* This memory is in render buffer memory pool. so we don't need to free those after loading. */ + /* This memory is in render buffer memory pool. so we don't need to free those after loading. + */ LineartObjectLoadTaskInfo *olti = lineart_mem_acquire( &rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count); @@ -2428,8 +2469,9 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), dot_f = dot_v3v3_db(Cv, tri->gn); /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_ - * faces in perspective mode would get erroneously caught in this condition where they really are - * legit faces that would produce occlusion, but haven't encountered those yet in my test files. + * faces in perspective mode would get erroneously caught in this condition where they really + * are legit faces that would produce occlusion, but haven't encountered those yet in my test + * files. */ if (fabs(dot_f) < FLT_EPSILON) { return false; @@ -2496,8 +2538,9 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), return false; \ } - /* Determine the pair of edges that the line has crossed. The "|" symbol in the comment indicates - * triangle boundary. DBL_TRIANGLE_LIM is needed to for floating point precision tolerance. */ + /* Determine the pair of edges that the line has crossed. The "|" symbol in the comment + * indicates triangle boundary. DBL_TRIANGLE_LIM is needed to for floating point precision + * tolerance. */ if (st_l == 2) { /* Left side is in the triangle. */ @@ -3158,6 +3201,14 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->force_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SMOOTH_SURFACES) != 0; rb->sharp_as_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SHARP_EDGES) != 0; + rb->chain_preserve_details = (lmd->calculation_flags & LRT_CHAIN_PRESERVE_DETAILS) != 0; + + /* This is used to limit calculation to a certain level to save time, lines who have higher + * occlusion levels will get ignored. */ + rb->max_occlusion_level = lmd->level_end_override; + + rb->use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0; + int16_t edge_types = lmd->edge_types_override; rb->use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; @@ -3171,6 +3222,8 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->filter_face_mark = (lmd->calculation_flags & LRT_FILTER_FACE_MARK) != 0; rb->filter_face_mark_boundaries = (lmd->calculation_flags & LRT_FILTER_FACE_MARK_BOUNDARIES) != 0; + rb->filter_face_mark_keep_contour = (lmd->calculation_flags & + LRT_FILTER_FACE_MARK_KEEP_CONTOUR) != 0; rb->chain_data_pool = &lc->chain_data_pool; @@ -4176,12 +4229,6 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, * See definition of LineartTriangleThread for details. */ rb->triangle_size = lineart_triangle_size_get(scene, rb); - /* This is used to limit calculation to a certain level to save time, lines who have higher - * occlusion levels will get ignored. */ - rb->max_occlusion_level = (lmd->flags & LRT_GPENCIL_USE_CACHE) ? - lmd->level_end_override : - (lmd->use_multiple_levels ? lmd->level_end : lmd->level_start); - /* FIXME(Yiming): See definition of int #LineartRenderBuffer::_source_type for detailed. */ rb->_source_type = lmd->source_type; rb->_source_collection = lmd->source_collection; @@ -4253,8 +4300,8 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, if (rb->chain_smooth_tolerance > FLT_EPSILON) { /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best - * effective range in image-space (Coordinate only goes from -1 to 1). This value is somewhat - * arbitrary, but works best for the moment. */ + * effective range in image-space (Coordinate only goes from -1 to 1). This value is + * somewhat arbitrary, but works best for the moment. */ MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50); } diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index bd40eb35123..43581ad48d9 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -937,6 +937,40 @@ static void read_vertex_creases(Mesh *mesh, mesh->cd_flag |= ME_CDFLAG_VERT_CREASE; } +static void read_edge_creases(Mesh *mesh, + const Int32ArraySamplePtr &indices, + const FloatArraySamplePtr &sharpnesses) +{ + if (!(indices && sharpnesses)) { + return; + } + + MEdge *edges = mesh->medge; + int totedge = mesh->totedge; + + for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) { + int v1 = (*indices)[i]; + int v2 = (*indices)[i + 1]; + + if (v2 < v1) { + /* It appears to be common to store edges with the smallest index first, in which case this + * prevents us from doing the second search below. */ + std::swap(v1, v2); + } + + MEdge *edge = find_edge(edges, totedge, v1, v2); + if (edge == nullptr) { + edge = find_edge(edges, totedge, v2, v1); + } + + if (edge) { + edge->crease = unit_float_to_uchar_clamp((*sharpnesses)[s]); + } + } + + mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE; +} + /* ************************************************************************** */ AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings) @@ -1000,36 +1034,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec return; } - /* Read egde creases. */ - Int32ArraySamplePtr indices = sample.getCreaseIndices(); - Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses(); - - if (indices && sharpnesses) { - MEdge *edges = mesh->medge; - int totedge = mesh->totedge; - - for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) { - int v1 = (*indices)[i]; - int v2 = (*indices)[i + 1]; - - if (v2 < v1) { - /* It appears to be common to store edges with the smallest index first, in which case this - * prevents us from doing the second search below. */ - std::swap(v1, v2); - } - - MEdge *edge = find_edge(edges, totedge, v1, v2); - if (edge == nullptr) { - edge = find_edge(edges, totedge, v2, v1); - } - - if (edge) { - edge->crease = unit_float_to_uchar_clamp((*sharpnesses)[s]); - } - } - - mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE; - } + read_edge_creases(mesh, sample.getCreaseIndices(), sample.getCreaseSharpnesses()); read_vertex_creases(mesh, sample.getCornerIndices(), sample.getCornerSharpnesses()); diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh index e88a76fc4e8..daae8ae52c8 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh @@ -26,6 +26,7 @@ #include <type_traits> #include "BLI_compiler_attrs.h" +#include "BLI_fileops.h" #include "BLI_string_ref.hh" #include "BLI_utility_mixins.hh" @@ -276,7 +277,7 @@ template<eFileType filetype> class FormattedFileHandler : NonCopyable, NonMovabl FormattedFileHandler(std::string outfile_path) noexcept(false) : outfile_path_(std::move(outfile_path)) { - outfile_ = std::fopen(outfile_path_.c_str(), "w"); + outfile_ = BLI_fopen(outfile_path_.c_str(), "w"); if (!outfile_) { throw std::system_error(errno, std::system_category(), "Cannot open file " + outfile_path_); } diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index 5dac913c902..89e1de49511 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -1,10 +1,8 @@ /* Apache License, Version 2.0 */ -#include <fstream> #include <gtest/gtest.h> #include <ios> #include <memory> -#include <sstream> #include <string> #include <system_error> @@ -185,15 +183,21 @@ static std::unique_ptr<OBJWriter> init_writer(const OBJExportParams ¶ms, } } -/* The following is relative to BKE_tempdir_base. */ -const char *const temp_file_path = "output.OBJ"; +/* The following is relative to BKE_tempdir_base. + * Use Latin Capital Letter A with Ogonek, Cyrillic Capital Letter Zhe + * at the end, to test I/O on non-English file names. */ +const char *const temp_file_path = "output\xc4\x84\xd0\x96.OBJ"; static std::string read_temp_file_in_string(const std::string &file_path) { - std::ifstream temp_stream(file_path); - std::ostringstream input_ss; - input_ss << temp_stream.rdbuf(); - return input_ss.str(); + std::string res; + size_t buffer_len; + void *buffer = BLI_file_read_text_as_mem(file_path.c_str(), 0, &buffer_len); + if (buffer != NULL) { + res.assign((const char *)buffer, buffer_len); + MEM_freeN(buffer); + } + return res; } TEST(obj_exporter_writer, header) diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 88eb164c2b4..c51a615bfb6 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -315,7 +315,8 @@ .opacity = 1.0f, \ .flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \ .crease_threshold = DEG2RAD(140.0f), \ - .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | LRT_USE_CREASE_ON_SHARP_EDGES, \ + .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | \ + LRT_USE_CREASE_ON_SHARP_EDGES | LRT_FILTER_FACE_MARK_KEEP_CONTOUR, \ .angle_splitting_threshold = DEG2RAD(60.0f), \ .chaining_image_threshold = 0.001f, \ .chain_smooth_tolerance = 0.2f,\ diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index d4440592a00..3e77d566d27 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -50,7 +50,10 @@ typedef enum eLineartMainFlags { LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15), LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16), LRT_USE_CUSTOM_CAMERA = (1 << 17), + LRT_FILTER_FACE_MARK_KEEP_CONTOUR = (1 << 18), + LRT_USE_BACK_FACE_CULLING = (1 << 19), LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20), + LRT_CHAIN_PRESERVE_DETAILS = (1 << 22), } eLineartMainFlags; typedef enum eLineartEdgeFlag { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 086da4c31ba..e4e594cd8cf 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -3211,6 +3211,12 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) prop, "Boundaries", "Filter feature lines based on face mark boundaries"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_face_mark_keep_contour", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "calculation_flags", LRT_FILTER_FACE_MARK_KEEP_CONTOUR); + RNA_def_property_ui_text(prop, "Keep Contour", "Preserve contour lines while filtering"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "chaining_image_threshold", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_ui_text( prop, @@ -3231,6 +3237,12 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) prop, "Use Geometry Space", "Use geometry distance for chaining instead of image space"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_detail_preserve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_CHAIN_PRESERVE_DETAILS); + RNA_def_property_ui_text( + prop, "Preserve Details", "Keep the zig-zag \"noise\" in initial chaining"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_overlap_edge_type_support", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_ALLOW_OVERLAP_EDGE_TYPES); RNA_def_property_ui_text(prop, @@ -3444,6 +3456,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) prop, "Image Boundary Trimming", "Trim all edges right at the boundary of image(including overscan region)"); + + prop = RNA_def_property(srna, "use_back_face_culling", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_BACK_FACE_CULLING); + RNA_def_property_ui_text( + prop, + "Back Face Culling", + "Remove all back faces to speed up calculation, this will create edges in " + "different occlusion levels than when disabled"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_invert_collection", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index 52d5f3e97ef..b713df05b80 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -48,17 +48,11 @@ #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" -Mesh *triangulate_mesh(Mesh *mesh, - const int quad_method, - const int ngon_method, - const int min_vertices, - const int flag); - -Mesh *triangulate_mesh(Mesh *mesh, - const int quad_method, - const int ngon_method, - const int min_vertices, - const int flag) +static Mesh *triangulate_mesh(Mesh *mesh, + const int quad_method, + const int ngon_method, + const int min_vertices, + const int flag) { Mesh *result; BMesh *bm; diff --git a/source/blender/modifiers/intern/MOD_weld.cc b/source/blender/modifiers/intern/MOD_weld.cc index 3a06c6a649b..2d4710ca71a 100644 --- a/source/blender/modifiers/intern/MOD_weld.cc +++ b/source/blender/modifiers/intern/MOD_weld.cc @@ -331,7 +331,7 @@ static void weld_assert_poly_no_vert_repetition(const WeldPoly &wp, } else { int i = 0; - while (weld_iter_loop_of_poly_next(&iter)) { + while (weld_iter_loop_of_poly_next(iter)) { verts[i++] = iter.v; } } @@ -386,7 +386,10 @@ static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(Span<int> vert_dest_map, for (const int i : vert_dest_map.index_range()) { if (vert_dest_map[i] != OUT_OF_CONTEXT) { - wvert.append({vert_dest_map[i], i}); + WeldVert wv{}; + wv.vert_dest = vert_dest_map[i]; + wv.vert_orig = i; + wvert.append(wv); } } return wvert; @@ -676,7 +679,7 @@ static void weld_edge_groups_setup(const int medge_len, /** \name Weld Poly and Loop API * \{ */ -static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, +static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter &iter, const WeldPoly &wp, Span<WeldLoop> wloop, Span<MLoop> mloop, @@ -687,19 +690,19 @@ static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, return false; } - iter->loop_start = wp.loop_start; - iter->loop_end = wp.loop_end; - iter->wloop = wloop; - iter->mloop = mloop; - iter->loop_map = loop_map; - iter->group = group_buffer; + iter.loop_start = wp.loop_start; + iter.loop_end = wp.loop_end; + iter.wloop = wloop; + iter.mloop = mloop; + iter.loop_map = loop_map; + iter.group = group_buffer; int group_len = 0; if (group_buffer) { /* First loop group needs more attention. */ int loop_start, loop_end, l; - loop_start = iter->loop_start; - loop_end = l = iter->loop_end; + loop_start = iter.loop_start; + loop_end = l = iter.loop_end; while (l >= loop_start) { const int loop_ctx = loop_map[l]; if (loop_ctx != OUT_OF_CONTEXT) { @@ -715,30 +718,30 @@ static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, group_len = loop_end - l; int i = 0; while (l < loop_end) { - iter->group[i++] = ++l; + iter.group[i++] = ++l; } } } - iter->group_len = group_len; + iter.group_len = group_len; - iter->l_next = iter->loop_start; + iter.l_next = iter.loop_start; #ifdef USE_WELD_DEBUG - iter->v = OUT_OF_CONTEXT; + iter.v = OUT_OF_CONTEXT; #endif return true; } -static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter) +static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter &iter) { - int loop_end = iter->loop_end; - Span<WeldLoop> wloop = iter->wloop; - Span<int> loop_map = iter->loop_map; - int l = iter->l_curr = iter->l_next; - if (l == iter->loop_start) { + const int loop_end = iter.loop_end; + Span<WeldLoop> wloop = iter.wloop; + Span<int> loop_map = iter.loop_map; + int l = iter.l_curr = iter.l_next; + if (l == iter.loop_start) { /* `grupo_len` is already calculated in the first loop */ } else { - iter->group_len = 0; + iter.group_len = 0; } while (l <= loop_end) { int l_next = l + 1; @@ -749,32 +752,32 @@ static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter) l_next = wl->loop_skip_to; } if (wl->flag == ELEM_COLLAPSED) { - if (iter->group) { - iter->group[iter->group_len++] = l; + if (iter.group) { + iter.group[iter.group_len++] = l; } l = l_next; continue; } #ifdef USE_WELD_DEBUG - BLI_assert(iter->v != wl->vert); + BLI_assert(iter.v != wl->vert); #endif - iter->v = wl->vert; - iter->e = wl->edge; - iter->type = 1; + iter.v = wl->vert; + iter.e = wl->edge; + iter.type = 1; } else { - const MLoop *ml = &iter->mloop[l]; + const MLoop &ml = iter.mloop[l]; #ifdef USE_WELD_DEBUG - BLI_assert((uint)iter->v != ml->v); + BLI_assert((uint)iter.v != ml.v); #endif - iter->v = ml->v; - iter->e = ml->e; - iter->type = 0; + iter.v = ml.v; + iter.e = ml.e; + iter.type = 0; } - if (iter->group) { - iter->group[iter->group_len++] = l; + if (iter.group) { + iter.group[iter.group_len++] = l; } - iter->l_next = l_next; + iter.l_next = l_next; return true; } @@ -1026,18 +1029,13 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, MutableSpan<WeldGroup> r_vlinks, WeldMesh *r_weld_mesh) { - int poly_kill_len, loop_kill_len, wpoly_len, wpoly_new_len; - - WeldPoly *wpoly_new; - WeldLoop *wl; - MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly; MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; - wpoly_new = r_weld_mesh->wpoly_new; - wpoly_len = r_weld_mesh->wpoly_len; - wpoly_new_len = 0; - poly_kill_len = 0; - loop_kill_len = 0; + WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; + int wpoly_len = r_weld_mesh->wpoly_len; + int wpoly_new_len = 0; + int poly_kill_len = 0; + int loop_kill_len = 0; Span<int> loop_map = r_weld_mesh->loop_map; @@ -1051,7 +1049,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, int poly_len = wp.len; int ctx_verts_len = 0; - wl = &wloop[ctx_loops_ofs]; + WeldLoop *wl = &wloop[ctx_loops_ofs]; for (int l = ctx_loops_len; l--; wl++) { const int edge_dest = wl->edge; if (edge_dest == ELEM_COLLAPSED) { @@ -1108,7 +1106,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, /* Setup Polygon Overlap. */ - int wpoly_and_new_len = wpoly_len + wpoly_new_len; + const int wpoly_and_new_len = wpoly_len + wpoly_new_len; r_vlinks.fill({0, 0}); MutableSpan<WeldGroup> v_links = r_vlinks; @@ -1116,8 +1114,8 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, for (const int i : IndexRange(wpoly_and_new_len)) { const WeldPoly &wp = wpoly[i]; WeldLoopOfPolyIter iter; - if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) { - while (weld_iter_loop_of_poly_next(&iter)) { + if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) { + while (weld_iter_loop_of_poly_next(iter)) { v_links[iter.v].len++; } } @@ -1135,8 +1133,8 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, for (const int i : IndexRange(wpoly_and_new_len)) { const WeldPoly &wp = wpoly[i]; WeldLoopOfPolyIter iter; - if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) { - while (weld_iter_loop_of_poly_next(&iter)) { + if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) { + while (weld_iter_loop_of_poly_next(iter)) { link_poly_buffer[v_links[iter.v].ofs++] = i; } } @@ -1159,8 +1157,8 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, } WeldLoopOfPolyIter iter; - weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr); - weld_iter_loop_of_poly_next(&iter); + weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr); + weld_iter_loop_of_poly_next(iter); struct WeldGroup *link_a = &v_links[iter.v]; polys_len_a = link_a->len; if (polys_len_a == 1) { @@ -1181,7 +1179,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, } WeldLoopOfPolyIter iter_b = iter; - while (weld_iter_loop_of_poly_next(&iter_b)) { + while (weld_iter_loop_of_poly_next(iter_b)) { struct WeldGroup *link_b = &v_links[iter_b.v]; polys_len_b = link_b->len; if (polys_len_b == 1) { @@ -1253,15 +1251,15 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, /** \name Weld Mesh API * \{ */ -static void weld_mesh_context_create(const Mesh *mesh, +static void weld_mesh_context_create(const Mesh &mesh, MutableSpan<int> vert_dest_map, const int vert_kill_len, WeldMesh *r_weld_mesh) { - Span<MEdge> medge{mesh->medge, mesh->totedge}; - Span<MPoly> mpoly{mesh->mpoly, mesh->totpoly}; - Span<MLoop> mloop{mesh->mloop, mesh->totloop}; - const int mvert_len = mesh->totvert; + Span<MEdge> medge{mesh.medge, mesh.totedge}; + Span<MPoly> mpoly{mesh.mpoly, mesh.totpoly}; + Span<MLoop> mloop{mesh.mloop, mesh.totloop}; + const int mvert_len = mesh.totvert; Vector<WeldVert> wvert = weld_vert_ctx_alloc_and_setup(vert_dest_map, vert_kill_len); r_weld_mesh->vert_kill_len = vert_kill_len; @@ -1686,7 +1684,7 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, } WeldMesh weld_mesh; - weld_mesh_context_create(mesh, vert_dest_map, vert_kill_len, &weld_mesh); + weld_mesh_context_create(*mesh, vert_dest_map, vert_kill_len, &weld_mesh); const int result_nverts = totvert - weld_mesh.vert_kill_len; const int result_nedges = totedge - weld_mesh.edge_kill_len; @@ -1740,7 +1738,7 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, dest_index = 0; for (int i = 0; i < totedge; i++) { - int source_index = i; + const int source_index = i; int count = 0; while (i < totedge && weld_mesh.edge_groups_map[i] == OUT_OF_CONTEXT) { edge_final[i] = dest_index + count; @@ -1787,8 +1785,8 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, Array<int, 64> group_buffer(weld_mesh.max_poly_len); for (const int i : mpoly.index_range()) { const MPoly &mp = mpoly[i]; - int loop_start = loop_cur; - int poly_ctx = weld_mesh.poly_map[i]; + const int loop_start = loop_cur; + const int poly_ctx = weld_mesh.poly_map[i]; if (poly_ctx == OUT_OF_CONTEXT) { int mp_loop_len = mp.totloop; CustomData_copy_data(&mesh->ldata, &result->ldata, mp.loopstart, loop_cur, mp_loop_len); @@ -1802,14 +1800,14 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, const WeldPoly &wp = weld_mesh.wpoly[poly_ctx]; WeldLoopOfPolyIter iter; if (!weld_iter_loop_of_poly_begin( - &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { + iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { continue; } if (wp.poly_dst != OUT_OF_CONTEXT) { continue; } - while (weld_iter_loop_of_poly_next(&iter)) { + while (weld_iter_loop_of_poly_next(iter)) { customdata_weld( &mesh->ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur); int v = vert_final[iter.v]; @@ -1834,17 +1832,17 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, for (const int i : IndexRange(weld_mesh.wpoly_new_len)) { const WeldPoly &wp = weld_mesh.wpoly_new[i]; - int loop_start = loop_cur; + const int loop_start = loop_cur; WeldLoopOfPolyIter iter; if (!weld_iter_loop_of_poly_begin( - &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { + iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) { continue; } if (wp.poly_dst != OUT_OF_CONTEXT) { continue; } - while (weld_iter_loop_of_poly_next(&iter)) { + while (weld_iter_loop_of_poly_next(iter)) { customdata_weld(&mesh->ldata, &result->ldata, group_buffer.data(), iter.group_len, loop_cur); int v = vert_final[iter.v]; int e = edge_final[iter.e]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc index c3c26736e88..fbf6b521fd8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -14,20 +14,96 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_spline.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_input_curve_handles_cc { static void node_declare(NodeDeclarationBuilder &b) { + b.add_input<decl::Bool>(N_("Relative")).default_value(false).supports_field(); b.add_output<decl::Vector>(N_("Left")).field_source(); b.add_output<decl::Vector>(N_("Right")).field_source(); } +class HandlePositionFieldInput final : public GeometryFieldInput { + Field<bool> relative_; + bool left_; + + public: + HandlePositionFieldInput(Field<bool> relative, bool left) + : GeometryFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left) + { + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask mask) const final + { + if (component.type() != GEO_COMPONENT_TYPE_CURVE) { + return {}; + } + + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator(field_context, &mask); + evaluator.add(relative_); + evaluator.evaluate(); + const VArray<bool> &relative = evaluator.get_evaluated<bool>(0); + + VArray<float3> positions = component.attribute_get_for_read<float3>( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + + StringRef side = left_ ? "handle_left" : "handle_right"; + VArray<float3> handles = component.attribute_get_for_read<float3>( + side, ATTR_DOMAIN_POINT, {0, 0, 0}); + + if (relative.is_single()) { + if (relative.get_internal_single()) { + Array<float3> output(positions.size()); + for (const int i : positions.index_range()) { + output[i] = handles[i] - positions[i]; + } + return component.attribute_try_adapt_domain<float3>( + VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); + } + return component.attribute_try_adapt_domain<float3>(handles, ATTR_DOMAIN_POINT, domain); + } + + Array<float3> output(positions.size()); + for (const int i : positions.index_range()) { + if (relative[i]) { + output[i] = handles[i] - positions[i]; + } + else { + output[i] = handles[i]; + } + } + return component.attribute_try_adapt_domain<float3>( + VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); + } + + uint64_t hash() const override + { + return get_default_hash_2(relative_, left_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const HandlePositionFieldInput *other_handle = + dynamic_cast<const HandlePositionFieldInput *>(&other)) { + return relative_ == other_handle->relative_ && left_ == other_handle->left_; + } + return false; + } +}; + static void node_geo_exec(GeoNodeExecParams params) { - Field<float3> left_field = AttributeFieldInput::Create<float3>("handle_left"); - Field<float3> right_field = AttributeFieldInput::Create<float3>("handle_right"); + Field<bool> relative = params.extract_input<Field<bool>>("Relative"); + Field<float3> left_field{std::make_shared<HandlePositionFieldInput>(relative, true)}; + Field<float3> right_field{std::make_shared<HandlePositionFieldInput>(relative, false)}; + params.set_output("Left", std::move(left_field)); params.set_output("Right", std::move(right_field)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 998e4d58bf3..e78c4d7bc35 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -14,24 +14,25 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_customdata.h" +#include "BKE_mesh.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "DNA_mesh_types.h" + #include "UI_interface.h" #include "UI_resources.h" #include "node_geometry_util.hh" -extern "C" { -Mesh *triangulate_mesh(Mesh *mesh, - const int quad_method, - const int ngon_method, - const int min_vertices, - const int flag); -} - namespace blender::nodes::node_geo_triangulate_cc { static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); b.add_input<decl::Int>(N_("Minimum Vertices")).default_value(4).min(4).max(10000); b.add_output<decl::Geometry>(N_("Mesh")); } @@ -48,9 +49,35 @@ static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node) node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY; } +static Mesh *triangulate_mesh_selection(const Mesh &mesh, + const int quad_method, + const int ngon_method, + const IndexMask selection, + const int min_vertices) +{ + CustomData_MeshMasks cd_mask_extra = { + CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, 0, CD_MASK_ORIGINDEX}; + BMeshCreateParams create_params{0}; + BMeshFromMeshParams from_mesh_params{true, 1, 1, 1, cd_mask_extra}; + BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params); + + /* Tag faces to be triangulated from the selection mask. */ + BM_mesh_elem_table_ensure(bm, BM_FACE); + for (int i_face : selection) { + BM_elem_flag_set(BM_face_at_index(bm, i_face), BM_ELEM_TAG, true); + } + + BM_mesh_triangulate(bm, quad_method, ngon_method, min_vertices, true, NULL, NULL, NULL); + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, &mesh); + BM_mesh_free(bm); + BKE_mesh_normals_tag_dirty(result); + return result; +} + static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4); GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>( @@ -59,12 +86,22 @@ static void node_geo_exec(GeoNodeExecParams params) params.node().custom2); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - /* #triangulate_mesh might modify the input mesh currently. */ - Mesh *mesh_in = geometry_set.get_mesh_for_write(); - if (mesh_in != nullptr) { - Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0); - geometry_set.replace_mesh(mesh_out); + if (!geometry_set.has_mesh()) { + return; } + GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>(); + const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); + + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator{context, domain_size}; + evaluator.add(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + + Mesh *mesh_out = triangulate_mesh_selection( + mesh_in, quad_method, ngon_method, selection, min_vertices); + geometry_set.replace_mesh(mesh_out); }); params.set_output("Mesh", std::move(geometry_set)); |