From 1c1dc5f8440d918aca480904c53e31a528aef42f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 29 Sep 2022 18:32:44 -0500 Subject: Cleanup: Move files that use mesh runtime data to C++ In preparation for moving the mesh runtime struct out of DNA. --- source/blender/blenkernel/BKE_editmesh.h | 2 +- source/blender/blenkernel/CMakeLists.txt | 4 +- source/blender/blenkernel/intern/editmesh.c | 319 ---------- source/blender/blenkernel/intern/editmesh.cc | 307 ++++++++++ source/blender/blenkernel/intern/mesh_iterators.c | 381 ------------ source/blender/blenkernel/intern/mesh_iterators.cc | 388 +++++++++++++ source/blender/draw/CMakeLists.txt | 2 +- source/blender/draw/intern/draw_manager_text.c | 637 -------------------- source/blender/draw/intern/draw_manager_text.cc | 639 +++++++++++++++++++++ source/blender/editors/include/ED_view3d.h | 2 +- source/blender/editors/space_view3d/CMakeLists.txt | 2 +- .../space_view3d/view3d_gizmo_preselect_type.c | 572 ------------------ .../space_view3d/view3d_gizmo_preselect_type.cc | 576 +++++++++++++++++++ source/blender/modifiers/CMakeLists.txt | 2 +- source/blender/modifiers/intern/MOD_wave.c | 495 ---------------- source/blender/modifiers/intern/MOD_wave.cc | 497 ++++++++++++++++ 16 files changed, 2414 insertions(+), 2411 deletions(-) delete mode 100644 source/blender/blenkernel/intern/editmesh.c create mode 100644 source/blender/blenkernel/intern/editmesh.cc delete mode 100644 source/blender/blenkernel/intern/mesh_iterators.c create mode 100644 source/blender/blenkernel/intern/mesh_iterators.cc delete mode 100644 source/blender/draw/intern/draw_manager_text.c create mode 100644 source/blender/draw/intern/draw_manager_text.cc delete mode 100644 source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c create mode 100644 source/blender/editors/space_view3d/view3d_gizmo_preselect_type.cc delete mode 100644 source/blender/modifiers/intern/MOD_wave.c create mode 100644 source/blender/modifiers/intern/MOD_wave.cc diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 5916e7e83fb..dbfc9fd0799 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -69,7 +69,7 @@ typedef struct BMEditMesh { } BMEditMesh; -/* editmesh.c */ +/* editmesh.cc */ void BKE_editmesh_looptri_calc_ex(BMEditMesh *em, const struct BMeshCalcTessellation_Params *params); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 4c5deead9d4..627e34be424 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -125,7 +125,7 @@ set(SRC intern/displist.cc intern/dynamicpaint.c intern/editlattice.c - intern/editmesh.c + intern/editmesh.cc intern/editmesh_bvh.c intern/editmesh_cache.cc intern/editmesh_tangent.cc @@ -199,7 +199,7 @@ set(SRC intern/mesh_debug.cc intern/mesh_evaluate.cc intern/mesh_fair.cc - intern/mesh_iterators.c + intern/mesh_iterators.cc intern/mesh_legacy_convert.cc intern/mesh_mapping.cc intern/mesh_merge.c diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c deleted file mode 100644 index a952da6fa52..00000000000 --- a/source/blender/blenkernel/intern/editmesh.c +++ /dev/null @@ -1,319 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup bke - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_listBase.h" -#include "DNA_mesh_types.h" -#include "DNA_object_types.h" - -#include "BLI_bitmap.h" -#include "BLI_math.h" - -#include "BKE_DerivedMesh.h" -#include "BKE_customdata.h" -#include "BKE_editmesh.h" -#include "BKE_editmesh_cache.h" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -#include "BKE_mesh_iterators.h" -#include "BKE_mesh_wrapper.h" -#include "BKE_object.h" - -#include "DEG_depsgraph_query.h" - -BMEditMesh *BKE_editmesh_create(BMesh *bm) -{ - BMEditMesh *em = MEM_callocN(sizeof(BMEditMesh), __func__); - em->bm = bm; - return em; -} - -BMEditMesh *BKE_editmesh_copy(BMEditMesh *em) -{ - BMEditMesh *em_copy = MEM_callocN(sizeof(BMEditMesh), __func__); - *em_copy = *em; - - em_copy->bm = BM_mesh_copy(em->bm); - - /* The tessellation is NOT calculated on the copy here, - * because currently all the callers of this function use - * it to make a backup copy of the #BMEditMesh to restore - * it in the case of errors in an operation. For performance reasons, - * in that case it makes more sense to do the - * tessellation only when/if that copy ends up getting used. */ - em_copy->looptris = NULL; - - /* Copy various settings. */ - em_copy->selectmode = em->selectmode; - em_copy->mat_nr = em->mat_nr; - - return em_copy; -} - -BMEditMesh *BKE_editmesh_from_object(Object *ob) -{ - BLI_assert(ob->type == OB_MESH); - /* sanity check */ -#if 0 /* disable in mutlti-object edit. */ -# ifndef NDEBUG - if (((Mesh *)ob->data)->edit_mesh) { - BLI_assert(((Mesh *)ob->data)->edit_mesh->ob == ob); - } -# endif -#endif - return ((Mesh *)ob->data)->edit_mesh; -} - -static void editmesh_tessface_calc_intern(BMEditMesh *em, - const struct BMeshCalcTessellation_Params *params) -{ - /* allocating space before calculating the tessellation */ - - BMesh *bm = em->bm; - - /* This assumes all faces can be scan-filled, which isn't always true, - * worst case we over allocate a little which is acceptable. */ - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - const int looptris_tot_prev_alloc = em->looptris ? - (MEM_allocN_len(em->looptris) / sizeof(*em->looptris)) : - 0; - - BMLoop *(*looptris)[3]; - - /* this means no reallocs for quad dominant models, for */ - if ((em->looptris != NULL) && - /* (*em->tottri >= looptris_tot)) */ - /* Check against allocated size in case we over allocated a little. */ - ((looptris_tot_prev_alloc >= looptris_tot) && - (looptris_tot_prev_alloc <= looptris_tot * 2))) { - looptris = em->looptris; - } - else { - if (em->looptris) { - MEM_freeN(em->looptris); - } - looptris = MEM_mallocN(sizeof(*looptris) * looptris_tot, __func__); - } - - em->looptris = looptris; - em->tottri = looptris_tot; - - /* after allocating the em->looptris, we're ready to tessellate */ - BM_mesh_calc_tessellation_ex(em->bm, em->looptris, params); -} - -void BKE_editmesh_looptri_calc_ex(BMEditMesh *em, - const struct BMeshCalcTessellation_Params *params) -{ - editmesh_tessface_calc_intern(em, params); - - /* commented because editbmesh_build_data() ensures we get tessfaces */ -#if 0 - if (em->mesh_eval_final && em->mesh_eval_final == em->mesh_eval_cage) { - BKE_mesh_runtime_looptri_ensure(em->mesh_eval_final); - } - else if (em->mesh_eval_final) { - BKE_mesh_runtime_looptri_ensure(em->mesh_eval_final); - BKE_mesh_runtime_looptri_ensure(em->mesh_eval_cage); - } -#endif -} - -void BKE_editmesh_looptri_calc(BMEditMesh *em) -{ - BKE_editmesh_looptri_calc_ex(em, - &(const struct BMeshCalcTessellation_Params){ - .face_normals = false, - }); -} - -void BKE_editmesh_looptri_and_normals_calc(BMEditMesh *em) -{ - BKE_editmesh_looptri_calc_ex(em, - &(const struct BMeshCalcTessellation_Params){ - .face_normals = true, - }); - BM_mesh_normals_update_ex(em->bm, - &(const struct BMeshNormalsUpdate_Params){ - .face_normals = false, - }); -} - -void BKE_editmesh_looptri_calc_with_partial_ex(BMEditMesh *em, - struct BMPartialUpdate *bmpinfo, - const struct BMeshCalcTessellation_Params *params) -{ - BLI_assert(em->tottri == poly_to_tri_count(em->bm->totface, em->bm->totloop)); - BLI_assert(em->looptris != NULL); - - BM_mesh_calc_tessellation_with_partial_ex(em->bm, em->looptris, bmpinfo, params); -} - -void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo) -{ - BKE_editmesh_looptri_calc_with_partial_ex(em, - bmpinfo, - &(const struct BMeshCalcTessellation_Params){ - .face_normals = false, - }); -} - -void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, - struct BMPartialUpdate *bmpinfo) -{ - BKE_editmesh_looptri_calc_with_partial_ex(em, - bmpinfo, - &(const struct BMeshCalcTessellation_Params){ - .face_normals = true, - }); - BM_mesh_normals_update_with_partial_ex(em->bm, - bmpinfo, - &(const struct BMeshNormalsUpdate_Params){ - .face_normals = false, - }); -} - -void BKE_editmesh_free_data(BMEditMesh *em) -{ - - if (em->looptris) { - MEM_freeN(em->looptris); - } - - if (em->bm) { - BM_mesh_free(em->bm); - } -} - -struct CageUserData { - int totvert; - float (*cos_cage)[3]; - BLI_bitmap *visit_bitmap; -}; - -static void cage_mapped_verts_callback(void *userData, - int index, - const float co[3], - const float UNUSED(no[3])) -{ - struct CageUserData *data = userData; - - if ((index >= 0 && index < data->totvert) && (!BLI_BITMAP_TEST(data->visit_bitmap, index))) { - BLI_BITMAP_ENABLE(data->visit_bitmap, index); - copy_v3_v3(data->cos_cage[index], co); - } -} - -float (*BKE_editmesh_vert_coords_alloc(struct Depsgraph *depsgraph, - BMEditMesh *em, - struct Scene *scene, - Object *ob, - int *r_vert_len))[3] -{ - Mesh *cage = editbmesh_get_eval_cage(depsgraph, scene, ob, em, &CD_MASK_BAREMESH); - float(*cos_cage)[3] = MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, "bmbvh cos_cage"); - - /* When initializing cage verts, we only want the first cage coordinate for each vertex, - * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate. */ - BLI_bitmap *visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__); - - struct CageUserData data; - data.totvert = em->bm->totvert; - data.cos_cage = cos_cage; - data.visit_bitmap = visit_bitmap; - - BKE_mesh_foreach_mapped_vert(cage, cage_mapped_verts_callback, &data, MESH_FOREACH_NOP); - - MEM_freeN(visit_bitmap); - - if (r_vert_len) { - *r_vert_len = em->bm->totvert; - } - - return cos_cage; -} - -const float (*BKE_editmesh_vert_coords_when_deformed(struct Depsgraph *depsgraph, - BMEditMesh *em, - struct Scene *scene, - Object *ob, - int *r_vert_len, - bool *r_is_alloc))[3] -{ - const float(*coords)[3] = NULL; - *r_is_alloc = false; - - Mesh *me = ob->data; - Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); - Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object_eval); - - if ((me->runtime.edit_data != NULL) && (me->runtime.edit_data->vertexCos != NULL)) { - /* Deformed, and we have deformed coords already. */ - coords = me->runtime.edit_data->vertexCos; - } - else if ((editmesh_eval_final != NULL) && - (editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { - /* If this is an edit-mesh type, leave NULL as we can use the vertex coords. */ - } - else { - /* Constructive modifiers have been used, we need to allocate coordinates. */ - *r_is_alloc = true; - coords = BKE_editmesh_vert_coords_alloc(depsgraph, em, scene, ob, r_vert_len); - } - return coords; -} - -float (*BKE_editmesh_vert_coords_alloc_orco(BMEditMesh *em, int *r_vert_len))[3] -{ - return BM_mesh_vert_coords_alloc(em->bm, r_vert_len); -} - -void BKE_editmesh_lnorspace_update(BMEditMesh *em, Mesh *me) -{ - BMesh *bm = em->bm; - - /* We need to create custom-loop-normals (CLNORS) data if none exist yet, - * otherwise there is no way to edit them. - * Similar code to #MESH_OT_customdata_custom_splitnormals_add operator, - * we want to keep same shading in case we were using auto-smooth so far. - * NOTE: there is a problem here, which is that if someone starts a normal editing operation on - * previously auto-smooth-ed mesh, and cancel that operation, generated CLNORS data remain, - * with related sharp edges (and hence auto-smooth is 'lost'). - * Not sure how critical this is, and how to fix that issue? */ - if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) { - if (me->flag & ME_AUTOSMOOTH) { - BM_edges_sharp_from_angle_set(bm, me->smoothresh); - } - } - - BM_lnorspace_update(bm); -} - -void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, Mesh *me) -{ - if (!(me->flag & ME_AUTOSMOOTH)) { - me->flag |= ME_AUTOSMOOTH; - BKE_editmesh_lnorspace_update(em, me); - } -} - -BoundBox *BKE_editmesh_cage_boundbox_get(struct Object *object, BMEditMesh *UNUSED(em)) -{ - if (object->runtime.editmesh_bb_cage == NULL) { - float min[3], max[3]; - INIT_MINMAX(min, max); - if (object->runtime.editmesh_eval_cage) { - BKE_mesh_wrapper_minmax(object->runtime.editmesh_eval_cage, min, max); - } - - object->runtime.editmesh_bb_cage = MEM_callocN(sizeof(BoundBox), "BMEditMesh.bb_cage"); - BKE_boundbox_init_from_minmax(object->runtime.editmesh_bb_cage, min, max); - } - - return object->runtime.editmesh_bb_cage; -} diff --git a/source/blender/blenkernel/intern/editmesh.cc b/source/blender/blenkernel/intern/editmesh.cc new file mode 100644 index 00000000000..deca24afc0f --- /dev/null +++ b/source/blender/blenkernel/intern/editmesh.cc @@ -0,0 +1,307 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" + +#include "BLI_bitmap.h" +#include "BLI_math.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_iterators.h" +#include "BKE_mesh_wrapper.h" +#include "BKE_object.h" + +#include "DEG_depsgraph_query.h" + +BMEditMesh *BKE_editmesh_create(BMesh *bm) +{ + BMEditMesh *em = MEM_cnew(__func__); + em->bm = bm; + return em; +} + +BMEditMesh *BKE_editmesh_copy(BMEditMesh *em) +{ + BMEditMesh *em_copy = MEM_cnew(__func__); + *em_copy = *em; + + em_copy->bm = BM_mesh_copy(em->bm); + + /* The tessellation is NOT calculated on the copy here, + * because currently all the callers of this function use + * it to make a backup copy of the #BMEditMesh to restore + * it in the case of errors in an operation. For performance reasons, + * in that case it makes more sense to do the + * tessellation only when/if that copy ends up getting used. */ + em_copy->looptris = nullptr; + + /* Copy various settings. */ + em_copy->selectmode = em->selectmode; + em_copy->mat_nr = em->mat_nr; + + return em_copy; +} + +BMEditMesh *BKE_editmesh_from_object(Object *ob) +{ + BLI_assert(ob->type == OB_MESH); + /* sanity check */ +#if 0 /* disable in mutlti-object edit. */ +# ifndef NDEBUG + if (((Mesh *)ob->data)->edit_mesh) { + BLI_assert(((Mesh *)ob->data)->edit_mesh->ob == ob); + } +# endif +#endif + return ((Mesh *)ob->data)->edit_mesh; +} + +static void editmesh_tessface_calc_intern(BMEditMesh *em, + const BMeshCalcTessellation_Params *params) +{ + /* allocating space before calculating the tessellation */ + + BMesh *bm = em->bm; + + /* This assumes all faces can be scan-filled, which isn't always true, + * worst case we over allocate a little which is acceptable. */ + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); + const int looptris_tot_prev_alloc = em->looptris ? + (MEM_allocN_len(em->looptris) / sizeof(*em->looptris)) : + 0; + + BMLoop *(*looptris)[3]; + + /* this means no reallocs for quad dominant models, for */ + if ((em->looptris != nullptr) && + /* (*em->tottri >= looptris_tot)) */ + /* Check against allocated size in case we over allocated a little. */ + ((looptris_tot_prev_alloc >= looptris_tot) && + (looptris_tot_prev_alloc <= looptris_tot * 2))) { + looptris = em->looptris; + } + else { + if (em->looptris) { + MEM_freeN(em->looptris); + } + looptris = static_cast( + MEM_mallocN(sizeof(*looptris) * looptris_tot, __func__)); + } + + em->looptris = looptris; + em->tottri = looptris_tot; + + /* after allocating the em->looptris, we're ready to tessellate */ + BM_mesh_calc_tessellation_ex(em->bm, em->looptris, params); +} + +void BKE_editmesh_looptri_calc_ex(BMEditMesh *em, const BMeshCalcTessellation_Params *params) +{ + editmesh_tessface_calc_intern(em, params); + + /* commented because editbmesh_build_data() ensures we get tessfaces */ +#if 0 + if (em->mesh_eval_final && em->mesh_eval_final == em->mesh_eval_cage) { + BKE_mesh_runtime_looptri_ensure(em->mesh_eval_final); + } + else if (em->mesh_eval_final) { + BKE_mesh_runtime_looptri_ensure(em->mesh_eval_final); + BKE_mesh_runtime_looptri_ensure(em->mesh_eval_cage); + } +#endif +} + +void BKE_editmesh_looptri_calc(BMEditMesh *em) +{ + BMeshCalcTessellation_Params params{}; + params.face_normals = false; + BKE_editmesh_looptri_calc_ex(em, ¶ms); +} + +void BKE_editmesh_looptri_and_normals_calc(BMEditMesh *em) +{ + BMeshCalcTessellation_Params looptri_params{}; + looptri_params.face_normals = true; + BKE_editmesh_looptri_calc_ex(em, &looptri_params); + BMeshNormalsUpdate_Params normals_params{}; + normals_params.face_normals = false; + BM_mesh_normals_update_ex(em->bm, &normals_params); +} + +void BKE_editmesh_looptri_calc_with_partial_ex(BMEditMesh *em, + BMPartialUpdate *bmpinfo, + const BMeshCalcTessellation_Params *params) +{ + BLI_assert(em->tottri == poly_to_tri_count(em->bm->totface, em->bm->totloop)); + BLI_assert(em->looptris != nullptr); + + BM_mesh_calc_tessellation_with_partial_ex(em->bm, em->looptris, bmpinfo, params); +} + +void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, BMPartialUpdate *bmpinfo) +{ + BMeshCalcTessellation_Params looptri_params{}; + looptri_params.face_normals = false; + BKE_editmesh_looptri_calc_with_partial_ex(em, bmpinfo, &looptri_params); +} + +void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, BMPartialUpdate *bmpinfo) +{ + BMeshCalcTessellation_Params looptri_params{}; + looptri_params.face_normals = true; + BKE_editmesh_looptri_calc_with_partial_ex(em, bmpinfo, &looptri_params); + BMeshNormalsUpdate_Params normals_params{}; + normals_params.face_normals = false; + BM_mesh_normals_update_with_partial_ex(em->bm, bmpinfo, &normals_params); +} + +void BKE_editmesh_free_data(BMEditMesh *em) +{ + + if (em->looptris) { + MEM_freeN(em->looptris); + } + + if (em->bm) { + BM_mesh_free(em->bm); + } +} + +struct CageUserData { + int totvert; + float (*cos_cage)[3]; + BLI_bitmap *visit_bitmap; +}; + +static void cage_mapped_verts_callback(void *userData, + int index, + const float co[3], + const float UNUSED(no[3])) +{ + CageUserData *data = static_cast(userData); + + if ((index >= 0 && index < data->totvert) && (!BLI_BITMAP_TEST(data->visit_bitmap, index))) { + BLI_BITMAP_ENABLE(data->visit_bitmap, index); + copy_v3_v3(data->cos_cage[index], co); + } +} + +float (*BKE_editmesh_vert_coords_alloc( + Depsgraph *depsgraph, BMEditMesh *em, Scene *scene, Object *ob, int *r_vert_len))[3] +{ + Mesh *cage = editbmesh_get_eval_cage(depsgraph, scene, ob, em, &CD_MASK_BAREMESH); + float(*cos_cage)[3] = static_cast( + MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, __func__)); + + /* When initializing cage verts, we only want the first cage coordinate for each vertex, + * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate. */ + BLI_bitmap *visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__); + + CageUserData data; + data.totvert = em->bm->totvert; + data.cos_cage = cos_cage; + data.visit_bitmap = visit_bitmap; + + BKE_mesh_foreach_mapped_vert(cage, cage_mapped_verts_callback, &data, MESH_FOREACH_NOP); + + MEM_freeN(visit_bitmap); + + if (r_vert_len) { + *r_vert_len = em->bm->totvert; + } + + return cos_cage; +} + +const float (*BKE_editmesh_vert_coords_when_deformed(Depsgraph *depsgraph, + BMEditMesh *em, + Scene *scene, + Object *ob, + int *r_vert_len, + bool *r_is_alloc))[3] +{ + const float(*coords)[3] = nullptr; + *r_is_alloc = false; + + Mesh *me = static_cast(ob->data); + Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); + Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object_eval); + + if ((me->runtime.edit_data != nullptr) && (me->runtime.edit_data->vertexCos != nullptr)) { + /* Deformed, and we have deformed coords already. */ + coords = me->runtime.edit_data->vertexCos; + } + else if ((editmesh_eval_final != nullptr) && + (editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { + /* If this is an edit-mesh type, leave nullptr as we can use the vertex coords. */ + } + else { + /* Constructive modifiers have been used, we need to allocate coordinates. */ + *r_is_alloc = true; + coords = BKE_editmesh_vert_coords_alloc(depsgraph, em, scene, ob, r_vert_len); + } + return coords; +} + +float (*BKE_editmesh_vert_coords_alloc_orco(BMEditMesh *em, int *r_vert_len))[3] +{ + return BM_mesh_vert_coords_alloc(em->bm, r_vert_len); +} + +void BKE_editmesh_lnorspace_update(BMEditMesh *em, Mesh *me) +{ + BMesh *bm = em->bm; + + /* We need to create custom-loop-normals (CLNORS) data if none exist yet, + * otherwise there is no way to edit them. + * Similar code to #MESH_OT_customdata_custom_splitnormals_add operator, + * we want to keep same shading in case we were using auto-smooth so far. + * NOTE: there is a problem here, which is that if someone starts a normal editing operation on + * previously auto-smooth-ed mesh, and cancel that operation, generated CLNORS data remain, + * with related sharp edges (and hence auto-smooth is 'lost'). + * Not sure how critical this is, and how to fix that issue? */ + if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) { + if (me->flag & ME_AUTOSMOOTH) { + BM_edges_sharp_from_angle_set(bm, me->smoothresh); + } + } + + BM_lnorspace_update(bm); +} + +void BKE_editmesh_ensure_autosmooth(BMEditMesh *em, Mesh *me) +{ + if (!(me->flag & ME_AUTOSMOOTH)) { + me->flag |= ME_AUTOSMOOTH; + BKE_editmesh_lnorspace_update(em, me); + } +} + +BoundBox *BKE_editmesh_cage_boundbox_get(Object *object, BMEditMesh *UNUSED(em)) +{ + if (object->runtime.editmesh_bb_cage == nullptr) { + float min[3], max[3]; + INIT_MINMAX(min, max); + if (object->runtime.editmesh_eval_cage) { + BKE_mesh_wrapper_minmax(object->runtime.editmesh_eval_cage, min, max); + } + + object->runtime.editmesh_bb_cage = MEM_cnew("BMEditMesh.bb_cage"); + BKE_boundbox_init_from_minmax(object->runtime.editmesh_bb_cage, min, max); + } + + return object->runtime.editmesh_bb_cage; +} diff --git a/source/blender/blenkernel/intern/mesh_iterators.c b/source/blender/blenkernel/intern/mesh_iterators.c deleted file mode 100644 index d3a7f6cc72f..00000000000 --- a/source/blender/blenkernel/intern/mesh_iterators.c +++ /dev/null @@ -1,381 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bke - * - * Functions for iterating mesh features. - */ - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_customdata.h" -#include "BKE_editmesh.h" -#include "BKE_editmesh_cache.h" -#include "BKE_mesh.h" -#include "BKE_mesh_iterators.h" - -#include "BLI_bitmap.h" -#include "BLI_math.h" - -#include "MEM_guardedalloc.h" - -/* General note on iterating verts/loops/edges/polys and end mode. - * - * The edit mesh pointer is set for both final and cage meshes in both cases when there are - * modifiers applied and not. This helps consistency of checks in the draw manager, where the - * existence of the edit mesh pointer does not depend on object configuration. - * - * For the iterating, however, we need to follow the `CD_ORIGINDEX` code paths when there are - * modifiers applied on the cage. In the code terms it means that the check for the edit mode code - * path needs to consist of both edit mesh and edit data checks. */ - -void BKE_mesh_foreach_mapped_vert( - Mesh *mesh, - void (*func)(void *userData, int index, const float co[3], const float no[3]), - void *userData, - MeshForeachFlag flag) -{ - if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) { - BMEditMesh *em = mesh->edit_mesh; - BMesh *bm = em->bm; - BMIter iter; - BMVert *eve; - int i; - if (mesh->runtime.edit_data->vertexCos != NULL) { - const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos; - const float(*vertexNos)[3]; - if (flag & MESH_FOREACH_USE_NORMAL) { - BKE_editmesh_cache_ensure_vert_normals(em, mesh->runtime.edit_data); - vertexNos = mesh->runtime.edit_data->vertexNos; - } - else { - vertexNos = NULL; - } - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? vertexNos[i] : NULL; - func(userData, i, vertexCos[i], no); - } - } - else { - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? eve->no : NULL; - func(userData, i, eve->co, no); - } - } - } - else { - const MVert *mv = BKE_mesh_verts(mesh); - const int *index = CustomData_get_layer(&mesh->vdata, CD_ORIGINDEX); - const float(*vert_normals)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? - BKE_mesh_vertex_normals_ensure(mesh) : - NULL; - - if (index) { - for (int i = 0; i < mesh->totvert; i++, mv++) { - const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[i] : NULL; - const int orig = *index++; - if (orig == ORIGINDEX_NONE) { - continue; - } - func(userData, orig, mv->co, no); - } - } - else { - for (int i = 0; i < mesh->totvert; i++, mv++) { - const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[i] : NULL; - func(userData, i, mv->co, no); - } - } - } -} - -void BKE_mesh_foreach_mapped_edge( - Mesh *mesh, - const int tot_edges, - void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]), - void *userData) -{ - if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) { - BMEditMesh *em = mesh->edit_mesh; - BMesh *bm = em->bm; - BMIter iter; - BMEdge *eed; - int i; - if (mesh->runtime.edit_data->vertexCos != NULL) { - const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos; - BM_mesh_elem_index_ensure(bm, BM_VERT); - - BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) { - func(userData, - i, - vertexCos[BM_elem_index_get(eed->v1)], - vertexCos[BM_elem_index_get(eed->v2)]); - } - } - else { - BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) { - func(userData, i, eed->v1->co, eed->v2->co); - } - } - } - else { - const MVert *mv = BKE_mesh_verts(mesh); - const MEdge *med = BKE_mesh_edges(mesh); - const int *index = CustomData_get_layer(&mesh->edata, CD_ORIGINDEX); - - if (index) { - for (int i = 0; i < mesh->totedge; i++, med++) { - const int orig = *index++; - if (orig == ORIGINDEX_NONE) { - continue; - } - func(userData, orig, mv[med->v1].co, mv[med->v2].co); - } - } - else if (mesh->totedge == tot_edges) { - for (int i = 0; i < mesh->totedge; i++, med++) { - func(userData, i, mv[med->v1].co, mv[med->v2].co); - } - } - } -} - -void BKE_mesh_foreach_mapped_loop(Mesh *mesh, - void (*func)(void *userData, - int vertex_index, - int face_index, - const float co[3], - const float no[3]), - void *userData, - MeshForeachFlag flag) -{ - - /* We can't use dm->getLoopDataLayout(dm) here, - * we want to always access dm->loopData, EditDerivedBMesh would - * return loop data from bmesh itself. */ - if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) { - BMEditMesh *em = mesh->edit_mesh; - BMesh *bm = em->bm; - BMIter iter; - BMFace *efa; - - const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos; - - /* XXX: investigate using EditMesh data. */ - const float(*lnors)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? - CustomData_get_layer(&mesh->ldata, CD_NORMAL) : - NULL; - - int f_idx; - - BM_mesh_elem_index_ensure(bm, BM_VERT); - - BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, f_idx) { - BMLoop *l_iter, *l_first; - - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const BMVert *eve = l_iter->v; - const int v_idx = BM_elem_index_get(eve); - const float *no = lnors ? *lnors++ : NULL; - func(userData, v_idx, f_idx, vertexCos ? vertexCos[v_idx] : eve->co, no); - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - const float(*lnors)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? - CustomData_get_layer(&mesh->ldata, CD_NORMAL) : - NULL; - - const MVert *mv = BKE_mesh_verts(mesh); - const MLoop *ml = BKE_mesh_loops(mesh); - const MPoly *mp = BKE_mesh_polys(mesh); - const int *v_index = CustomData_get_layer(&mesh->vdata, CD_ORIGINDEX); - const int *f_index = CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX); - int p_idx, i; - - if (v_index || f_index) { - for (p_idx = 0; p_idx < mesh->totpoly; p_idx++, mp++) { - for (i = 0; i < mp->totloop; i++, ml++) { - const int v_idx = v_index ? v_index[ml->v] : ml->v; - const int f_idx = f_index ? f_index[p_idx] : p_idx; - const float *no = lnors ? *lnors++ : NULL; - if (ELEM(ORIGINDEX_NONE, v_idx, f_idx)) { - continue; - } - func(userData, v_idx, f_idx, mv[ml->v].co, no); - } - } - } - else { - for (p_idx = 0; p_idx < mesh->totpoly; p_idx++, mp++) { - for (i = 0; i < mp->totloop; i++, ml++) { - const int v_idx = ml->v; - const int f_idx = p_idx; - const float *no = lnors ? *lnors++ : NULL; - func(userData, v_idx, f_idx, mv[ml->v].co, no); - } - } - } - } -} - -void BKE_mesh_foreach_mapped_face_center( - Mesh *mesh, - void (*func)(void *userData, int index, const float cent[3], const float no[3]), - void *userData, - MeshForeachFlag flag) -{ - if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) { - BMEditMesh *em = mesh->edit_mesh; - BMesh *bm = em->bm; - const float(*polyCos)[3]; - const float(*polyNos)[3]; - BMFace *efa; - BMIter iter; - int i; - - BKE_editmesh_cache_ensure_poly_centers(em, mesh->runtime.edit_data); - polyCos = mesh->runtime.edit_data->polyCos; /* always set */ - - if (flag & MESH_FOREACH_USE_NORMAL) { - BKE_editmesh_cache_ensure_poly_normals(em, mesh->runtime.edit_data); - polyNos = mesh->runtime.edit_data->polyNos; /* maybe NULL */ - } - else { - polyNos = NULL; - } - - if (polyNos) { - BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - const float *no = polyNos[i]; - func(userData, i, polyCos[i], no); - } - } - else { - BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? efa->no : NULL; - func(userData, i, polyCos[i], no); - } - } - } - else { - const MVert *mvert = BKE_mesh_verts(mesh); - const MPoly *mp = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); - const MLoop *ml; - float _no_buf[3]; - float *no = (flag & MESH_FOREACH_USE_NORMAL) ? _no_buf : NULL; - const int *index = CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX); - - if (index) { - for (int i = 0; i < mesh->totpoly; i++, mp++) { - const int orig = *index++; - if (orig == ORIGINDEX_NONE) { - continue; - } - float cent[3]; - ml = &loops[mp->loopstart]; - BKE_mesh_calc_poly_center(mp, ml, mvert, cent); - if (flag & MESH_FOREACH_USE_NORMAL) { - BKE_mesh_calc_poly_normal(mp, ml, mvert, no); - } - func(userData, orig, cent, no); - } - } - else { - for (int i = 0; i < mesh->totpoly; i++, mp++) { - float cent[3]; - ml = &loops[mp->loopstart]; - BKE_mesh_calc_poly_center(mp, ml, mvert, cent); - if (flag & MESH_FOREACH_USE_NORMAL) { - BKE_mesh_calc_poly_normal(mp, ml, mvert, no); - } - func(userData, i, cent, no); - } - } - } -} - -void BKE_mesh_foreach_mapped_subdiv_face_center( - Mesh *mesh, - void (*func)(void *userData, int index, const float cent[3], const float no[3]), - void *userData, - MeshForeachFlag flag) -{ - const MVert *verts = BKE_mesh_verts(mesh); - const MPoly *mp = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); - const MLoop *ml; - const MVert *mv; - const float(*vert_normals)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? - BKE_mesh_vertex_normals_ensure(mesh) : - NULL; - const int *index = CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX); - const BLI_bitmap *facedot_tags = mesh->runtime.subsurf_face_dot_tags; - BLI_assert(facedot_tags != NULL); - - if (index) { - for (int i = 0; i < mesh->totpoly; i++, mp++) { - const int orig = *index++; - if (orig == ORIGINDEX_NONE) { - continue; - } - ml = &loops[mp->loopstart]; - for (int j = 0; j < mp->totloop; j++, ml++) { - mv = &verts[ml->v]; - if (BLI_BITMAP_TEST(facedot_tags, ml->v)) { - func(userData, - orig, - mv->co, - (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[ml->v] : NULL); - } - } - } - } - else { - for (int i = 0; i < mesh->totpoly; i++, mp++) { - ml = &loops[mp->loopstart]; - for (int j = 0; j < mp->totloop; j++, ml++) { - mv = &verts[ml->v]; - if (BLI_BITMAP_TEST(facedot_tags, ml->v)) { - func(userData, i, mv->co, (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[ml->v] : NULL); - } - } - } - } -} - -/* Helpers based on above foreach loopers> */ - -typedef struct MappedVCosData { - float (*vertexcos)[3]; - BLI_bitmap *vertex_visit; -} MappedVCosData; - -static void get_vertexcos__mapFunc(void *user_data, - int index, - const float co[3], - const float UNUSED(no[3])) -{ - MappedVCosData *mapped_vcos_data = (MappedVCosData *)user_data; - - if (BLI_BITMAP_TEST(mapped_vcos_data->vertex_visit, index) == 0) { - /* We need coord from prototype vertex, not from copies, - * we assume they stored in the beginning of vertex array stored in evaluated mesh - * (mirror modifier for eg does this). */ - copy_v3_v3(mapped_vcos_data->vertexcos[index], co); - BLI_BITMAP_ENABLE(mapped_vcos_data->vertex_visit, index); - } -} - -void BKE_mesh_foreach_mapped_vert_coords_get(Mesh *me_eval, float (*r_cos)[3], const int totcos) -{ - MappedVCosData user_data; - memset(r_cos, 0, sizeof(*r_cos) * totcos); - user_data.vertexcos = r_cos; - user_data.vertex_visit = BLI_BITMAP_NEW(totcos, __func__); - BKE_mesh_foreach_mapped_vert(me_eval, get_vertexcos__mapFunc, &user_data, MESH_FOREACH_NOP); - MEM_freeN(user_data.vertex_visit); -} diff --git a/source/blender/blenkernel/intern/mesh_iterators.cc b/source/blender/blenkernel/intern/mesh_iterators.cc new file mode 100644 index 00000000000..281c84a3df5 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_iterators.cc @@ -0,0 +1,388 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + * + * Functions for iterating mesh features. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_mesh.h" +#include "BKE_mesh_iterators.h" + +#include "BLI_bitmap.h" +#include "BLI_math.h" + +#include "MEM_guardedalloc.h" + +/* General note on iterating verts/loops/edges/polys and end mode. + * + * The edit mesh pointer is set for both final and cage meshes in both cases when there are + * modifiers applied and not. This helps consistency of checks in the draw manager, where the + * existence of the edit mesh pointer does not depend on object configuration. + * + * For the iterating, however, we need to follow the `CD_ORIGINDEX` code paths when there are + * modifiers applied on the cage. In the code terms it means that the check for the edit mode code + * path needs to consist of both edit mesh and edit data checks. */ + +void BKE_mesh_foreach_mapped_vert( + Mesh *mesh, + void (*func)(void *userData, int index, const float co[3], const float no[3]), + void *userData, + MeshForeachFlag flag) +{ + if (mesh->edit_mesh != nullptr && mesh->runtime.edit_data != nullptr) { + BMEditMesh *em = mesh->edit_mesh; + BMesh *bm = em->bm; + BMIter iter; + BMVert *eve; + int i; + if (mesh->runtime.edit_data->vertexCos != nullptr) { + const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos; + const float(*vertexNos)[3]; + if (flag & MESH_FOREACH_USE_NORMAL) { + BKE_editmesh_cache_ensure_vert_normals(em, mesh->runtime.edit_data); + vertexNos = mesh->runtime.edit_data->vertexNos; + } + else { + vertexNos = nullptr; + } + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? vertexNos[i] : nullptr; + func(userData, i, vertexCos[i], no); + } + } + else { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? eve->no : nullptr; + func(userData, i, eve->co, no); + } + } + } + else { + const MVert *mv = BKE_mesh_verts(mesh); + const int *index = static_cast(CustomData_get_layer(&mesh->vdata, CD_ORIGINDEX)); + const float(*vert_normals)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? + BKE_mesh_vertex_normals_ensure(mesh) : + nullptr; + + if (index) { + for (int i = 0; i < mesh->totvert; i++, mv++) { + const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[i] : nullptr; + const int orig = *index++; + if (orig == ORIGINDEX_NONE) { + continue; + } + func(userData, orig, mv->co, no); + } + } + else { + for (int i = 0; i < mesh->totvert; i++, mv++) { + const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[i] : nullptr; + func(userData, i, mv->co, no); + } + } + } +} + +void BKE_mesh_foreach_mapped_edge( + Mesh *mesh, + const int tot_edges, + void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]), + void *userData) +{ + if (mesh->edit_mesh != nullptr && mesh->runtime.edit_data) { + BMEditMesh *em = mesh->edit_mesh; + BMesh *bm = em->bm; + BMIter iter; + BMEdge *eed; + int i; + if (mesh->runtime.edit_data->vertexCos != nullptr) { + const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos; + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) { + func(userData, + i, + vertexCos[BM_elem_index_get(eed->v1)], + vertexCos[BM_elem_index_get(eed->v2)]); + } + } + else { + BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) { + func(userData, i, eed->v1->co, eed->v2->co); + } + } + } + else { + const MVert *mv = BKE_mesh_verts(mesh); + const MEdge *med = BKE_mesh_edges(mesh); + const int *index = static_cast(CustomData_get_layer(&mesh->edata, CD_ORIGINDEX)); + + if (index) { + for (int i = 0; i < mesh->totedge; i++, med++) { + const int orig = *index++; + if (orig == ORIGINDEX_NONE) { + continue; + } + func(userData, orig, mv[med->v1].co, mv[med->v2].co); + } + } + else if (mesh->totedge == tot_edges) { + for (int i = 0; i < mesh->totedge; i++, med++) { + func(userData, i, mv[med->v1].co, mv[med->v2].co); + } + } + } +} + +void BKE_mesh_foreach_mapped_loop(Mesh *mesh, + void (*func)(void *userData, + int vertex_index, + int face_index, + const float co[3], + const float no[3]), + void *userData, + MeshForeachFlag flag) +{ + + /* We can't use dm->getLoopDataLayout(dm) here, + * we want to always access dm->loopData, EditDerivedBMesh would + * return loop data from bmesh itself. */ + if (mesh->edit_mesh != nullptr && mesh->runtime.edit_data) { + BMEditMesh *em = mesh->edit_mesh; + BMesh *bm = em->bm; + BMIter iter; + BMFace *efa; + + const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos; + + /* XXX: investigate using EditMesh data. */ + const float(*lnors)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? + static_cast( + CustomData_get_layer(&mesh->ldata, CD_NORMAL)) : + nullptr; + + int f_idx; + + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, f_idx) { + BMLoop *l_iter, *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const BMVert *eve = l_iter->v; + const int v_idx = BM_elem_index_get(eve); + const float *no = lnors ? *lnors++ : nullptr; + func(userData, v_idx, f_idx, vertexCos ? vertexCos[v_idx] : eve->co, no); + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + const float(*lnors)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? + static_cast( + CustomData_get_layer(&mesh->ldata, CD_NORMAL)) : + nullptr; + + const MVert *mv = BKE_mesh_verts(mesh); + const MLoop *ml = BKE_mesh_loops(mesh); + const MPoly *mp = BKE_mesh_polys(mesh); + const int *v_index = static_cast( + CustomData_get_layer(&mesh->vdata, CD_ORIGINDEX)); + const int *f_index = static_cast( + CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX)); + int p_idx, i; + + if (v_index || f_index) { + for (p_idx = 0; p_idx < mesh->totpoly; p_idx++, mp++) { + for (i = 0; i < mp->totloop; i++, ml++) { + const int v_idx = v_index ? v_index[ml->v] : ml->v; + const int f_idx = f_index ? f_index[p_idx] : p_idx; + const float *no = lnors ? *lnors++ : nullptr; + if (ELEM(ORIGINDEX_NONE, v_idx, f_idx)) { + continue; + } + func(userData, v_idx, f_idx, mv[ml->v].co, no); + } + } + } + else { + for (p_idx = 0; p_idx < mesh->totpoly; p_idx++, mp++) { + for (i = 0; i < mp->totloop; i++, ml++) { + const int v_idx = ml->v; + const int f_idx = p_idx; + const float *no = lnors ? *lnors++ : nullptr; + func(userData, v_idx, f_idx, mv[ml->v].co, no); + } + } + } + } +} + +void BKE_mesh_foreach_mapped_face_center( + Mesh *mesh, + void (*func)(void *userData, int index, const float cent[3], const float no[3]), + void *userData, + MeshForeachFlag flag) +{ + if (mesh->edit_mesh != nullptr && mesh->runtime.edit_data != nullptr) { + BMEditMesh *em = mesh->edit_mesh; + BMesh *bm = em->bm; + const float(*polyCos)[3]; + const float(*polyNos)[3]; + BMFace *efa; + BMIter iter; + int i; + + BKE_editmesh_cache_ensure_poly_centers(em, mesh->runtime.edit_data); + polyCos = mesh->runtime.edit_data->polyCos; /* always set */ + + if (flag & MESH_FOREACH_USE_NORMAL) { + BKE_editmesh_cache_ensure_poly_normals(em, mesh->runtime.edit_data); + polyNos = mesh->runtime.edit_data->polyNos; /* maybe nullptr */ + } + else { + polyNos = nullptr; + } + + if (polyNos) { + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { + const float *no = polyNos[i]; + func(userData, i, polyCos[i], no); + } + } + else { + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { + const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? efa->no : nullptr; + func(userData, i, polyCos[i], no); + } + } + } + else { + const MVert *mvert = BKE_mesh_verts(mesh); + const MPoly *mp = BKE_mesh_polys(mesh); + const MLoop *loops = BKE_mesh_loops(mesh); + const MLoop *ml; + float _no_buf[3]; + float *no = (flag & MESH_FOREACH_USE_NORMAL) ? _no_buf : nullptr; + const int *index = static_cast(CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX)); + + if (index) { + for (int i = 0; i < mesh->totpoly; i++, mp++) { + const int orig = *index++; + if (orig == ORIGINDEX_NONE) { + continue; + } + float cent[3]; + ml = &loops[mp->loopstart]; + BKE_mesh_calc_poly_center(mp, ml, mvert, cent); + if (flag & MESH_FOREACH_USE_NORMAL) { + BKE_mesh_calc_poly_normal(mp, ml, mvert, no); + } + func(userData, orig, cent, no); + } + } + else { + for (int i = 0; i < mesh->totpoly; i++, mp++) { + float cent[3]; + ml = &loops[mp->loopstart]; + BKE_mesh_calc_poly_center(mp, ml, mvert, cent); + if (flag & MESH_FOREACH_USE_NORMAL) { + BKE_mesh_calc_poly_normal(mp, ml, mvert, no); + } + func(userData, i, cent, no); + } + } + } +} + +void BKE_mesh_foreach_mapped_subdiv_face_center( + Mesh *mesh, + void (*func)(void *userData, int index, const float cent[3], const float no[3]), + void *userData, + MeshForeachFlag flag) +{ + const MVert *verts = BKE_mesh_verts(mesh); + const MPoly *mp = BKE_mesh_polys(mesh); + const MLoop *loops = BKE_mesh_loops(mesh); + const MLoop *ml; + const MVert *mv; + const float(*vert_normals)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? + BKE_mesh_vertex_normals_ensure(mesh) : + nullptr; + const int *index = static_cast(CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX)); + const BLI_bitmap *facedot_tags = mesh->runtime.subsurf_face_dot_tags; + BLI_assert(facedot_tags != nullptr); + + if (index) { + for (int i = 0; i < mesh->totpoly; i++, mp++) { + const int orig = *index++; + if (orig == ORIGINDEX_NONE) { + continue; + } + ml = &loops[mp->loopstart]; + for (int j = 0; j < mp->totloop; j++, ml++) { + mv = &verts[ml->v]; + if (BLI_BITMAP_TEST(facedot_tags, ml->v)) { + func(userData, + orig, + mv->co, + (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[ml->v] : nullptr); + } + } + } + } + else { + for (int i = 0; i < mesh->totpoly; i++, mp++) { + ml = &loops[mp->loopstart]; + for (int j = 0; j < mp->totloop; j++, ml++) { + mv = &verts[ml->v]; + if (BLI_BITMAP_TEST(facedot_tags, ml->v)) { + func(userData, + i, + mv->co, + (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[ml->v] : nullptr); + } + } + } + } +} + +/* Helpers based on above foreach loopers> */ + +struct MappedVCosData { + float (*vertexcos)[3]; + BLI_bitmap *vertex_visit; +}; + +static void get_vertexcos__mapFunc(void *user_data, + int index, + const float co[3], + const float UNUSED(no[3])) +{ + MappedVCosData *mapped_vcos_data = (MappedVCosData *)user_data; + + if (BLI_BITMAP_TEST(mapped_vcos_data->vertex_visit, index) == 0) { + /* We need coord from prototype vertex, not from copies, + * we assume they stored in the beginning of vertex array stored in evaluated mesh + * (mirror modifier for eg does this). */ + copy_v3_v3(mapped_vcos_data->vertexcos[index], co); + BLI_BITMAP_ENABLE(mapped_vcos_data->vertex_visit, index); + } +} + +void BKE_mesh_foreach_mapped_vert_coords_get(Mesh *me_eval, float (*r_cos)[3], const int totcos) +{ + MappedVCosData user_data; + memset(r_cos, 0, sizeof(*r_cos) * totcos); + user_data.vertexcos = r_cos; + user_data.vertex_visit = BLI_BITMAP_NEW(totcos, __func__); + BKE_mesh_foreach_mapped_vert(me_eval, get_vertexcos__mapFunc, &user_data, MESH_FOREACH_NOP); + MEM_freeN(user_data.vertex_visit); +} diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 2acff89ce7e..1f47496ae82 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -92,7 +92,7 @@ set(SRC intern/draw_manager_exec.c intern/draw_manager_profiling.c intern/draw_manager_shader.c - intern/draw_manager_text.c + intern/draw_manager_text.cc intern/draw_manager_texture.c intern/draw_pbvh.cc intern/draw_select_buffer.c diff --git a/source/blender/draw/intern/draw_manager_text.c b/source/blender/draw/intern/draw_manager_text.c deleted file mode 100644 index 8987a6e2b20..00000000000 --- a/source/blender/draw/intern/draw_manager_text.c +++ /dev/null @@ -1,637 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2016 Blender Foundation. */ - -/** \file - * \ingroup draw - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_math.h" -#include "BLI_memiter.h" -#include "BLI_rect.h" -#include "BLI_string.h" - -#include "BKE_editmesh.h" -#include "BKE_editmesh_cache.h" -#include "BKE_global.h" -#include "BKE_unit.h" - -#include "DNA_mesh_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_view3d_types.h" - -#include "GPU_matrix.h" -#include "GPU_state.h" - -#include "ED_screen.h" -#include "ED_view3d.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "BLF_api.h" -#include "WM_api.h" - -#include "draw_manager_text.h" -#include "intern/bmesh_polygon.h" - -typedef struct ViewCachedString { - float vec[3]; - union { - uchar ub[4]; - int pack; - } col; - short sco[2]; - short xoffs, yoffs; - short flag; - int str_len; - - /* str is allocated past the end */ - char str[0]; -} ViewCachedString; - -typedef struct DRWTextStore { - BLI_memiter *cache_strings; -} DRWTextStore; - -DRWTextStore *DRW_text_cache_create(void) -{ - DRWTextStore *dt = MEM_callocN(sizeof(*dt), __func__); - dt->cache_strings = BLI_memiter_create(1 << 14); /* 16kb */ - return dt; -} - -void DRW_text_cache_destroy(struct DRWTextStore *dt) -{ - BLI_memiter_destroy(dt->cache_strings); - MEM_freeN(dt); -} - -void DRW_text_cache_add(DRWTextStore *dt, - const float co[3], - const char *str, - const int str_len, - short xoffs, - short yoffs, - short flag, - const uchar col[4]) -{ - int alloc_len; - ViewCachedString *vos; - - if (flag & DRW_TEXT_CACHE_STRING_PTR) { - BLI_assert(str_len == strlen(str)); - alloc_len = sizeof(void *); - } - else { - alloc_len = str_len + 1; - } - - vos = BLI_memiter_alloc(dt->cache_strings, sizeof(ViewCachedString) + alloc_len); - - copy_v3_v3(vos->vec, co); - copy_v4_v4_uchar(vos->col.ub, col); - vos->xoffs = xoffs; - vos->yoffs = yoffs; - vos->flag = flag; - vos->str_len = str_len; - - /* allocate past the end */ - if (flag & DRW_TEXT_CACHE_STRING_PTR) { - memcpy(vos->str, &str, alloc_len); - } - else { - memcpy(vos->str, str, alloc_len); - } -} - -static void drw_text_cache_draw_ex(DRWTextStore *dt, ARegion *region) -{ - ViewCachedString *vos; - BLI_memiter_handle it; - int col_pack_prev = 0; - - float original_proj[4][4]; - GPU_matrix_projection_get(original_proj); - wmOrtho2_region_pixelspace(region); - - GPU_matrix_push(); - GPU_matrix_identity_set(); - - const int font_id = BLF_default(); - - const uiStyle *style = UI_style_get(); - - BLF_size(font_id, style->widget.points * U.dpi_fac); - - BLI_memiter_iter_init(dt->cache_strings, &it); - while ((vos = BLI_memiter_iter_step(&it))) { - if (vos->sco[0] != IS_CLIPPED) { - if (col_pack_prev != vos->col.pack) { - BLF_color4ubv(font_id, vos->col.ub); - col_pack_prev = vos->col.pack; - } - - BLF_position( - font_id, (float)(vos->sco[0] + vos->xoffs), (float)(vos->sco[1] + vos->yoffs), 2.0f); - BLF_draw(font_id, - (vos->flag & DRW_TEXT_CACHE_STRING_PTR) ? *((const char **)vos->str) : vos->str, - vos->str_len); - } - } - - GPU_matrix_pop(); - GPU_matrix_projection_set(original_proj); -} - -void DRW_text_cache_draw(DRWTextStore *dt, ARegion *region, struct View3D *v3d) -{ - ViewCachedString *vos; - if (v3d) { - RegionView3D *rv3d = region->regiondata; - int tot = 0; - /* project first and test */ - BLI_memiter_handle it; - BLI_memiter_iter_init(dt->cache_strings, &it); - while ((vos = BLI_memiter_iter_step(&it))) { - if (ED_view3d_project_short_ex( - region, - (vos->flag & DRW_TEXT_CACHE_GLOBALSPACE) ? rv3d->persmat : rv3d->persmatob, - (vos->flag & DRW_TEXT_CACHE_LOCALCLIP) != 0, - vos->vec, - vos->sco, - V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) == - V3D_PROJ_RET_OK) { - tot++; - } - else { - vos->sco[0] = IS_CLIPPED; - } - } - - if (tot) { - /* Disable clipping for text */ - const bool rv3d_clipping_enabled = RV3D_CLIPPING_ENABLED(v3d, rv3d); - if (rv3d_clipping_enabled) { - GPU_clip_distances(0); - } - - drw_text_cache_draw_ex(dt, region); - - if (rv3d_clipping_enabled) { - GPU_clip_distances(6); - } - } - } - else { - /* project first */ - BLI_memiter_handle it; - BLI_memiter_iter_init(dt->cache_strings, &it); - View2D *v2d = ®ion->v2d; - float viewmat[4][4]; - rctf region_space = {0.0f, region->winx, 0.0f, region->winy}; - BLI_rctf_transform_calc_m4_pivot_min(&v2d->cur, ®ion_space, viewmat); - - while ((vos = BLI_memiter_iter_step(&it))) { - float p[3]; - copy_v3_v3(p, vos->vec); - mul_m4_v3(viewmat, p); - - vos->sco[0] = p[0]; - vos->sco[1] = p[1]; - } - - drw_text_cache_draw_ex(dt, region); - } -} - -void DRW_text_edit_mesh_measure_stats(ARegion *region, - View3D *v3d, - Object *ob, - const UnitSettings *unit) -{ - /* Do not use ascii when using non-default unit system, some unit chars are utf8 (micro, square, - * etc.). See bug T36090. - */ - struct DRWTextStore *dt = DRW_text_cache_ensure(); - const short txt_flag = DRW_TEXT_CACHE_GLOBALSPACE; - Mesh *me = ob->data; - BMEditMesh *em = me->edit_mesh; - float v1[3], v2[3], v3[3], vmid[3], fvec[3]; - char numstr[32]; /* Stores the measurement display text here */ - size_t numstr_len; - const char *conv_float; /* Use a float conversion matching the grid size */ - uchar col[4] = {0, 0, 0, 255}; /* color of the text to draw */ - float area; /* area of the face */ - float grid = unit->system ? unit->scale_length : v3d->grid; - const bool do_global = (v3d->flag & V3D_GLOBAL_STATS) != 0; - const bool do_moving = (G.moving & G_TRANSFORM_EDIT) != 0; - float clip_planes[4][4]; - /* allow for displaying shape keys and deform mods */ - BMIter iter; - const float(*vert_coords)[3] = (me->runtime.edit_data ? me->runtime.edit_data->vertexCos : NULL); - const bool use_coords = (vert_coords != NULL); - - /* when 2 or more edge-info options are enabled, space apart */ - short edge_tex_count = 0; - if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN) { - edge_tex_count += 1; - } - if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_ANG) { - edge_tex_count += 1; - } - if ((v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_INDICES) && (em->selectmode & SCE_SELECT_EDGE)) { - edge_tex_count += 1; - } - const short edge_tex_sep = (short)((edge_tex_count - 1) * 5.0f * U.dpi_fac); - - /* Make the precision of the display value proportionate to the grid-size. */ - - if (grid <= 0.01f) { - conv_float = "%.6g"; - } - else if (grid <= 0.1f) { - conv_float = "%.5g"; - } - else if (grid <= 1.0f) { - conv_float = "%.4g"; - } - else if (grid <= 10.0f) { - conv_float = "%.3g"; - } - else { - conv_float = "%.2g"; - } - - if (v3d->overlay.edit_flag & - (V3D_OVERLAY_EDIT_EDGE_LEN | V3D_OVERLAY_EDIT_EDGE_ANG | V3D_OVERLAY_EDIT_INDICES)) { - BoundBox bb; - const rcti rect = {0, region->winx, 0, region->winy}; - - ED_view3d_clipping_calc(&bb, clip_planes, region, ob, &rect); - } - - if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN) { - BMEdge *eed; - - UI_GetThemeColor3ubv(TH_DRAWEXTRA_EDGELEN, col); - - if (use_coords) { - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - } - - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - /* draw selected edges, or edges next to selected verts while dragging */ - if (BM_elem_flag_test(eed, BM_ELEM_SELECT) || - (do_moving && (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || - BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)))) { - float v1_clip[3], v2_clip[3]; - - if (vert_coords) { - copy_v3_v3(v1, vert_coords[BM_elem_index_get(eed->v1)]); - copy_v3_v3(v2, vert_coords[BM_elem_index_get(eed->v2)]); - } - else { - copy_v3_v3(v1, eed->v1->co); - copy_v3_v3(v2, eed->v2->co); - } - - if (clip_segment_v3_plane_n(v1, v2, clip_planes, 4, v1_clip, v2_clip)) { - - mid_v3_v3v3(vmid, v1_clip, v2_clip); - mul_m4_v3(ob->obmat, vmid); - - if (do_global) { - mul_mat3_m4_v3(ob->obmat, v1); - mul_mat3_m4_v3(ob->obmat, v2); - } - - if (unit->system) { - numstr_len = BKE_unit_value_as_string(numstr, - sizeof(numstr), - len_v3v3(v1, v2) * unit->scale_length, - 3, - B_UNIT_LENGTH, - unit, - false); - } - else { - numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), conv_float, len_v3v3(v1, v2)); - } - - DRW_text_cache_add(dt, vmid, numstr, numstr_len, 0, edge_tex_sep, txt_flag, col); - } - } - } - } - - if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_ANG) { - const bool is_rad = (unit->system_rotation == USER_UNIT_ROT_RADIANS); - BMEdge *eed; - - UI_GetThemeColor3ubv(TH_DRAWEXTRA_EDGEANG, col); - - const float(*poly_normals)[3] = NULL; - if (use_coords) { - BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); - BKE_editmesh_cache_ensure_poly_normals(em, me->runtime.edit_data); - poly_normals = me->runtime.edit_data->polyNos; - } - - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - BMLoop *l_a, *l_b; - if (BM_edge_loop_pair(eed, &l_a, &l_b)) { - /* Draw selected edges, or edges next to selected verts while dragging. */ - if (BM_elem_flag_test(eed, BM_ELEM_SELECT) || - (do_moving && (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || - BM_elem_flag_test(eed->v2, BM_ELEM_SELECT) || - /* Special case, this is useful to show when verts connected - * to this edge via a face are being transformed. */ - BM_elem_flag_test(l_a->next->next->v, BM_ELEM_SELECT) || - BM_elem_flag_test(l_a->prev->v, BM_ELEM_SELECT) || - BM_elem_flag_test(l_b->next->next->v, BM_ELEM_SELECT) || - BM_elem_flag_test(l_b->prev->v, BM_ELEM_SELECT)))) { - float v1_clip[3], v2_clip[3]; - - if (vert_coords) { - copy_v3_v3(v1, vert_coords[BM_elem_index_get(eed->v1)]); - copy_v3_v3(v2, vert_coords[BM_elem_index_get(eed->v2)]); - } - else { - copy_v3_v3(v1, eed->v1->co); - copy_v3_v3(v2, eed->v2->co); - } - - if (clip_segment_v3_plane_n(v1, v2, clip_planes, 4, v1_clip, v2_clip)) { - float no_a[3], no_b[3]; - float angle; - - mid_v3_v3v3(vmid, v1_clip, v2_clip); - mul_m4_v3(ob->obmat, vmid); - - if (use_coords) { - copy_v3_v3(no_a, poly_normals[BM_elem_index_get(l_a->f)]); - copy_v3_v3(no_b, poly_normals[BM_elem_index_get(l_b->f)]); - } - else { - copy_v3_v3(no_a, l_a->f->no); - copy_v3_v3(no_b, l_b->f->no); - } - - if (do_global) { - mul_mat3_m4_v3(ob->imat, no_a); - mul_mat3_m4_v3(ob->imat, no_b); - normalize_v3(no_a); - normalize_v3(no_b); - } - - angle = angle_normalized_v3v3(no_a, no_b); - - numstr_len = BLI_snprintf_rlen(numstr, - sizeof(numstr), - "%.3f%s", - (is_rad) ? angle : RAD2DEGF(angle), - (is_rad) ? "r" : "°"); - - DRW_text_cache_add(dt, vmid, numstr, numstr_len, 0, -edge_tex_sep, txt_flag, col); - } - } - } - } - } - - if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_AREA) { - /* would be nice to use BM_face_calc_area, but that is for 2d faces - * so instead add up tessellation triangle areas */ - - UI_GetThemeColor3ubv(TH_DRAWEXTRA_FACEAREA, col); - - int i, n; - BMFace *f = NULL; - /* Alternative to using `poly_to_tri_count(i, BM_elem_index_get(f->l_first))` - * without having to add an extra loop. */ - int looptri_index = 0; - BM_ITER_MESH_INDEX (f, &iter, em->bm, BM_FACES_OF_MESH, i) { - const int f_looptri_len = f->len - 2; - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - n = 0; - area = 0; - zero_v3(vmid); - BMLoop *(*l)[3] = &em->looptris[looptri_index]; - for (int j = 0; j < f_looptri_len; j++) { - - if (use_coords) { - copy_v3_v3(v1, vert_coords[BM_elem_index_get(l[j][0]->v)]); - copy_v3_v3(v2, vert_coords[BM_elem_index_get(l[j][1]->v)]); - copy_v3_v3(v3, vert_coords[BM_elem_index_get(l[j][2]->v)]); - } - else { - copy_v3_v3(v1, l[j][0]->v->co); - copy_v3_v3(v2, l[j][1]->v->co); - copy_v3_v3(v3, l[j][2]->v->co); - } - - add_v3_v3(vmid, v1); - add_v3_v3(vmid, v2); - add_v3_v3(vmid, v3); - n += 3; - - if (do_global) { - mul_mat3_m4_v3(ob->obmat, v1); - mul_mat3_m4_v3(ob->obmat, v2); - mul_mat3_m4_v3(ob->obmat, v3); - } - - area += area_tri_v3(v1, v2, v3); - } - - mul_v3_fl(vmid, 1.0f / (float)n); - mul_m4_v3(ob->obmat, vmid); - - if (unit->system) { - numstr_len = BKE_unit_value_as_string( - numstr, - sizeof(numstr), - (double)(area * unit->scale_length * unit->scale_length), - 3, - B_UNIT_AREA, - unit, - false); - } - else { - numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), conv_float, area); - } - - DRW_text_cache_add(dt, vmid, numstr, numstr_len, 0, 0, txt_flag, col); - } - looptri_index += f_looptri_len; - } - } - - if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_ANG) { - BMFace *efa; - const bool is_rad = (unit->system_rotation == USER_UNIT_ROT_RADIANS); - - UI_GetThemeColor3ubv(TH_DRAWEXTRA_FACEANG, col); - - if (use_coords) { - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - } - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - const bool is_face_sel = BM_elem_flag_test_bool(efa, BM_ELEM_SELECT); - - if (is_face_sel || do_moving) { - BMIter liter; - BMLoop *loop; - bool is_first = true; - - BM_ITER_ELEM (loop, &liter, efa, BM_LOOPS_OF_FACE) { - if (is_face_sel || (do_moving && (BM_elem_flag_test(loop->v, BM_ELEM_SELECT) || - BM_elem_flag_test(loop->prev->v, BM_ELEM_SELECT) || - BM_elem_flag_test(loop->next->v, BM_ELEM_SELECT)))) { - float v2_local[3]; - - /* lazy init center calc */ - if (is_first) { - if (use_coords) { - BM_face_calc_center_bounds_vcos(em->bm, efa, vmid, vert_coords); - } - else { - BM_face_calc_center_bounds(efa, vmid); - } - is_first = false; - } - if (use_coords) { - copy_v3_v3(v1, vert_coords[BM_elem_index_get(loop->prev->v)]); - copy_v3_v3(v2, vert_coords[BM_elem_index_get(loop->v)]); - copy_v3_v3(v3, vert_coords[BM_elem_index_get(loop->next->v)]); - } - else { - copy_v3_v3(v1, loop->prev->v->co); - copy_v3_v3(v2, loop->v->co); - copy_v3_v3(v3, loop->next->v->co); - } - - copy_v3_v3(v2_local, v2); - - if (do_global) { - mul_mat3_m4_v3(ob->obmat, v1); - mul_mat3_m4_v3(ob->obmat, v2); - mul_mat3_m4_v3(ob->obmat, v3); - } - - float angle = angle_v3v3v3(v1, v2, v3); - - numstr_len = BLI_snprintf_rlen(numstr, - sizeof(numstr), - "%.3f%s", - (is_rad) ? angle : RAD2DEGF(angle), - (is_rad) ? "r" : "°"); - interp_v3_v3v3(fvec, vmid, v2_local, 0.8f); - mul_m4_v3(ob->obmat, fvec); - DRW_text_cache_add(dt, fvec, numstr, numstr_len, 0, 0, txt_flag, col); - } - } - } - } - } - - /* This option is for mesh ops and addons debugging; only available in UI if Blender starts with - * --debug */ - if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_INDICES) { - int i; - - /* For now, reuse an appropriate theme color */ - UI_GetThemeColor3ubv(TH_DRAWEXTRA_FACEANG, col); - - if (em->selectmode & SCE_SELECT_VERTEX) { - BMVert *v; - - if (use_coords) { - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - } - BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, i) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { - if (use_coords) { - copy_v3_v3(v1, vert_coords[BM_elem_index_get(v)]); - } - else { - copy_v3_v3(v1, v->co); - } - - mul_m4_v3(ob->obmat, v1); - - numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", i); - DRW_text_cache_add(dt, v1, numstr, numstr_len, 0, 0, txt_flag, col); - } - } - } - - if (em->selectmode & SCE_SELECT_EDGE) { - BMEdge *eed; - - const bool use_edge_tex_sep = (edge_tex_count == 2); - const bool use_edge_tex_len = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN); - - BM_ITER_MESH_INDEX (eed, &iter, em->bm, BM_EDGES_OF_MESH, i) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - float v1_clip[3], v2_clip[3]; - - if (use_coords) { - copy_v3_v3(v1, vert_coords[BM_elem_index_get(eed->v1)]); - copy_v3_v3(v2, vert_coords[BM_elem_index_get(eed->v2)]); - } - else { - copy_v3_v3(v1, eed->v1->co); - copy_v3_v3(v2, eed->v2->co); - } - - if (clip_segment_v3_plane_n(v1, v2, clip_planes, 4, v1_clip, v2_clip)) { - mid_v3_v3v3(vmid, v1_clip, v2_clip); - mul_m4_v3(ob->obmat, vmid); - - numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", i); - DRW_text_cache_add( - dt, - vmid, - numstr, - numstr_len, - 0, - (use_edge_tex_sep) ? (use_edge_tex_len) ? -edge_tex_sep : edge_tex_sep : 0, - txt_flag, - col); - } - } - } - } - - if (em->selectmode & SCE_SELECT_FACE) { - BMFace *f; - - if (use_coords) { - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - } - - BM_ITER_MESH_INDEX (f, &iter, em->bm, BM_FACES_OF_MESH, i) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - - if (use_coords) { - BM_face_calc_center_median_vcos(em->bm, f, v1, vert_coords); - } - else { - BM_face_calc_center_median(f, v1); - } - - mul_m4_v3(ob->obmat, v1); - - numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", i); - DRW_text_cache_add(dt, v1, numstr, numstr_len, 0, 0, txt_flag, col); - } - } - } - } -} diff --git a/source/blender/draw/intern/draw_manager_text.cc b/source/blender/draw/intern/draw_manager_text.cc new file mode 100644 index 00000000000..e09050877c9 --- /dev/null +++ b/source/blender/draw/intern/draw_manager_text.cc @@ -0,0 +1,639 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2016 Blender Foundation. */ + +/** \file + * \ingroup draw + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_memiter.h" +#include "BLI_rect.h" +#include "BLI_string.h" + +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_global.h" +#include "BKE_unit.h" + +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" + +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BLF_api.h" +#include "WM_api.h" + +#include "draw_manager_text.h" +#include "intern/bmesh_polygon.h" + +struct ViewCachedString { + float vec[3]; + union { + uchar ub[4]; + int pack; + } col; + short sco[2]; + short xoffs, yoffs; + short flag; + int str_len; + + /* str is allocated past the end */ + char str[0]; +}; + +struct DRWTextStore { + BLI_memiter *cache_strings; +}; + +DRWTextStore *DRW_text_cache_create(void) +{ + DRWTextStore *dt = MEM_cnew(__func__); + dt->cache_strings = BLI_memiter_create(1 << 14); /* 16kb */ + return dt; +} + +void DRW_text_cache_destroy(DRWTextStore *dt) +{ + BLI_memiter_destroy(dt->cache_strings); + MEM_freeN(dt); +} + +void DRW_text_cache_add(DRWTextStore *dt, + const float co[3], + const char *str, + const int str_len, + short xoffs, + short yoffs, + short flag, + const uchar col[4]) +{ + int alloc_len; + ViewCachedString *vos; + + if (flag & DRW_TEXT_CACHE_STRING_PTR) { + BLI_assert(str_len == strlen(str)); + alloc_len = sizeof(void *); + } + else { + alloc_len = str_len + 1; + } + + vos = static_cast( + BLI_memiter_alloc(dt->cache_strings, sizeof(ViewCachedString) + alloc_len)); + + copy_v3_v3(vos->vec, co); + copy_v4_v4_uchar(vos->col.ub, col); + vos->xoffs = xoffs; + vos->yoffs = yoffs; + vos->flag = flag; + vos->str_len = str_len; + + /* allocate past the end */ + if (flag & DRW_TEXT_CACHE_STRING_PTR) { + memcpy(vos->str, &str, alloc_len); + } + else { + memcpy(vos->str, str, alloc_len); + } +} + +static void drw_text_cache_draw_ex(DRWTextStore *dt, ARegion *region) +{ + ViewCachedString *vos; + BLI_memiter_handle it; + int col_pack_prev = 0; + + float original_proj[4][4]; + GPU_matrix_projection_get(original_proj); + wmOrtho2_region_pixelspace(region); + + GPU_matrix_push(); + GPU_matrix_identity_set(); + + const int font_id = BLF_default(); + + const uiStyle *style = UI_style_get(); + + BLF_size(font_id, style->widget.points * U.dpi_fac); + + BLI_memiter_iter_init(dt->cache_strings, &it); + while ((vos = static_cast(BLI_memiter_iter_step(&it)))) { + if (vos->sco[0] != IS_CLIPPED) { + if (col_pack_prev != vos->col.pack) { + BLF_color4ubv(font_id, vos->col.ub); + col_pack_prev = vos->col.pack; + } + + BLF_position( + font_id, (float)(vos->sco[0] + vos->xoffs), (float)(vos->sco[1] + vos->yoffs), 2.0f); + BLF_draw(font_id, + (vos->flag & DRW_TEXT_CACHE_STRING_PTR) ? *((const char **)vos->str) : vos->str, + vos->str_len); + } + } + + GPU_matrix_pop(); + GPU_matrix_projection_set(original_proj); +} + +void DRW_text_cache_draw(DRWTextStore *dt, ARegion *region, View3D *v3d) +{ + ViewCachedString *vos; + if (v3d) { + RegionView3D *rv3d = static_cast(region->regiondata); + int tot = 0; + /* project first and test */ + BLI_memiter_handle it; + BLI_memiter_iter_init(dt->cache_strings, &it); + while ((vos = static_cast(BLI_memiter_iter_step(&it)))) { + if (ED_view3d_project_short_ex( + region, + (vos->flag & DRW_TEXT_CACHE_GLOBALSPACE) ? rv3d->persmat : rv3d->persmatob, + (vos->flag & DRW_TEXT_CACHE_LOCALCLIP) != 0, + vos->vec, + vos->sco, + V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) == + V3D_PROJ_RET_OK) { + tot++; + } + else { + vos->sco[0] = IS_CLIPPED; + } + } + + if (tot) { + /* Disable clipping for text */ + const bool rv3d_clipping_enabled = RV3D_CLIPPING_ENABLED(v3d, rv3d); + if (rv3d_clipping_enabled) { + GPU_clip_distances(0); + } + + drw_text_cache_draw_ex(dt, region); + + if (rv3d_clipping_enabled) { + GPU_clip_distances(6); + } + } + } + else { + /* project first */ + BLI_memiter_handle it; + BLI_memiter_iter_init(dt->cache_strings, &it); + View2D *v2d = ®ion->v2d; + float viewmat[4][4]; + rctf region_space = {0.0f, float(region->winx), 0.0f, float(region->winy)}; + BLI_rctf_transform_calc_m4_pivot_min(&v2d->cur, ®ion_space, viewmat); + + while ((vos = static_cast(BLI_memiter_iter_step(&it)))) { + float p[3]; + copy_v3_v3(p, vos->vec); + mul_m4_v3(viewmat, p); + + vos->sco[0] = p[0]; + vos->sco[1] = p[1]; + } + + drw_text_cache_draw_ex(dt, region); + } +} + +void DRW_text_edit_mesh_measure_stats(ARegion *region, + View3D *v3d, + Object *ob, + const UnitSettings *unit) +{ + /* Do not use ascii when using non-default unit system, some unit chars are utf8 (micro, square, + * etc.). See bug T36090. + */ + DRWTextStore *dt = DRW_text_cache_ensure(); + const short txt_flag = DRW_TEXT_CACHE_GLOBALSPACE; + Mesh *me = static_cast(ob->data); + BMEditMesh *em = me->edit_mesh; + float v1[3], v2[3], v3[3], vmid[3], fvec[3]; + char numstr[32]; /* Stores the measurement display text here */ + size_t numstr_len; + const char *conv_float; /* Use a float conversion matching the grid size */ + uchar col[4] = {0, 0, 0, 255}; /* color of the text to draw */ + float area; /* area of the face */ + float grid = unit->system ? unit->scale_length : v3d->grid; + const bool do_global = (v3d->flag & V3D_GLOBAL_STATS) != 0; + const bool do_moving = (G.moving & G_TRANSFORM_EDIT) != 0; + float clip_planes[4][4]; + /* allow for displaying shape keys and deform mods */ + BMIter iter; + const float(*vert_coords)[3] = (me->runtime.edit_data ? me->runtime.edit_data->vertexCos : + nullptr); + const bool use_coords = (vert_coords != nullptr); + + /* when 2 or more edge-info options are enabled, space apart */ + short edge_tex_count = 0; + if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN) { + edge_tex_count += 1; + } + if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_ANG) { + edge_tex_count += 1; + } + if ((v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_INDICES) && (em->selectmode & SCE_SELECT_EDGE)) { + edge_tex_count += 1; + } + const short edge_tex_sep = (short)((edge_tex_count - 1) * 5.0f * U.dpi_fac); + + /* Make the precision of the display value proportionate to the grid-size. */ + + if (grid <= 0.01f) { + conv_float = "%.6g"; + } + else if (grid <= 0.1f) { + conv_float = "%.5g"; + } + else if (grid <= 1.0f) { + conv_float = "%.4g"; + } + else if (grid <= 10.0f) { + conv_float = "%.3g"; + } + else { + conv_float = "%.2g"; + } + + if (v3d->overlay.edit_flag & + (V3D_OVERLAY_EDIT_EDGE_LEN | V3D_OVERLAY_EDIT_EDGE_ANG | V3D_OVERLAY_EDIT_INDICES)) { + BoundBox bb; + const rcti rect = {0, region->winx, 0, region->winy}; + + ED_view3d_clipping_calc(&bb, clip_planes, region, ob, &rect); + } + + if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN) { + BMEdge *eed; + + UI_GetThemeColor3ubv(TH_DRAWEXTRA_EDGELEN, col); + + if (use_coords) { + BM_mesh_elem_index_ensure(em->bm, BM_VERT); + } + + BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { + /* draw selected edges, or edges next to selected verts while dragging */ + if (BM_elem_flag_test(eed, BM_ELEM_SELECT) || + (do_moving && (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || + BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)))) { + float v1_clip[3], v2_clip[3]; + + if (vert_coords) { + copy_v3_v3(v1, vert_coords[BM_elem_index_get(eed->v1)]); + copy_v3_v3(v2, vert_coords[BM_elem_index_get(eed->v2)]); + } + else { + copy_v3_v3(v1, eed->v1->co); + copy_v3_v3(v2, eed->v2->co); + } + + if (clip_segment_v3_plane_n(v1, v2, clip_planes, 4, v1_clip, v2_clip)) { + + mid_v3_v3v3(vmid, v1_clip, v2_clip); + mul_m4_v3(ob->obmat, vmid); + + if (do_global) { + mul_mat3_m4_v3(ob->obmat, v1); + mul_mat3_m4_v3(ob->obmat, v2); + } + + if (unit->system) { + numstr_len = BKE_unit_value_as_string(numstr, + sizeof(numstr), + len_v3v3(v1, v2) * unit->scale_length, + 3, + B_UNIT_LENGTH, + unit, + false); + } + else { + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), conv_float, len_v3v3(v1, v2)); + } + + DRW_text_cache_add(dt, vmid, numstr, numstr_len, 0, edge_tex_sep, txt_flag, col); + } + } + } + } + + if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_ANG) { + const bool is_rad = (unit->system_rotation == USER_UNIT_ROT_RADIANS); + BMEdge *eed; + + UI_GetThemeColor3ubv(TH_DRAWEXTRA_EDGEANG, col); + + const float(*poly_normals)[3] = nullptr; + if (use_coords) { + BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); + BKE_editmesh_cache_ensure_poly_normals(em, me->runtime.edit_data); + poly_normals = me->runtime.edit_data->polyNos; + } + + BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { + BMLoop *l_a, *l_b; + if (BM_edge_loop_pair(eed, &l_a, &l_b)) { + /* Draw selected edges, or edges next to selected verts while dragging. */ + if (BM_elem_flag_test(eed, BM_ELEM_SELECT) || + (do_moving && (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || + BM_elem_flag_test(eed->v2, BM_ELEM_SELECT) || + /* Special case, this is useful to show when verts connected + * to this edge via a face are being transformed. */ + BM_elem_flag_test(l_a->next->next->v, BM_ELEM_SELECT) || + BM_elem_flag_test(l_a->prev->v, BM_ELEM_SELECT) || + BM_elem_flag_test(l_b->next->next->v, BM_ELEM_SELECT) || + BM_elem_flag_test(l_b->prev->v, BM_ELEM_SELECT)))) { + float v1_clip[3], v2_clip[3]; + + if (vert_coords) { + copy_v3_v3(v1, vert_coords[BM_elem_index_get(eed->v1)]); + copy_v3_v3(v2, vert_coords[BM_elem_index_get(eed->v2)]); + } + else { + copy_v3_v3(v1, eed->v1->co); + copy_v3_v3(v2, eed->v2->co); + } + + if (clip_segment_v3_plane_n(v1, v2, clip_planes, 4, v1_clip, v2_clip)) { + float no_a[3], no_b[3]; + float angle; + + mid_v3_v3v3(vmid, v1_clip, v2_clip); + mul_m4_v3(ob->obmat, vmid); + + if (use_coords) { + copy_v3_v3(no_a, poly_normals[BM_elem_index_get(l_a->f)]); + copy_v3_v3(no_b, poly_normals[BM_elem_index_get(l_b->f)]); + } + else { + copy_v3_v3(no_a, l_a->f->no); + copy_v3_v3(no_b, l_b->f->no); + } + + if (do_global) { + mul_mat3_m4_v3(ob->imat, no_a); + mul_mat3_m4_v3(ob->imat, no_b); + normalize_v3(no_a); + normalize_v3(no_b); + } + + angle = angle_normalized_v3v3(no_a, no_b); + + numstr_len = BLI_snprintf_rlen(numstr, + sizeof(numstr), + "%.3f%s", + (is_rad) ? angle : RAD2DEGF(angle), + (is_rad) ? "r" : "°"); + + DRW_text_cache_add(dt, vmid, numstr, numstr_len, 0, -edge_tex_sep, txt_flag, col); + } + } + } + } + } + + if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_AREA) { + /* would be nice to use BM_face_calc_area, but that is for 2d faces + * so instead add up tessellation triangle areas */ + + UI_GetThemeColor3ubv(TH_DRAWEXTRA_FACEAREA, col); + + int i, n; + BMFace *f = nullptr; + /* Alternative to using `poly_to_tri_count(i, BM_elem_index_get(f->l_first))` + * without having to add an extra loop. */ + int looptri_index = 0; + BM_ITER_MESH_INDEX (f, &iter, em->bm, BM_FACES_OF_MESH, i) { + const int f_looptri_len = f->len - 2; + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + n = 0; + area = 0; + zero_v3(vmid); + BMLoop *(*l)[3] = &em->looptris[looptri_index]; + for (int j = 0; j < f_looptri_len; j++) { + + if (use_coords) { + copy_v3_v3(v1, vert_coords[BM_elem_index_get(l[j][0]->v)]); + copy_v3_v3(v2, vert_coords[BM_elem_index_get(l[j][1]->v)]); + copy_v3_v3(v3, vert_coords[BM_elem_index_get(l[j][2]->v)]); + } + else { + copy_v3_v3(v1, l[j][0]->v->co); + copy_v3_v3(v2, l[j][1]->v->co); + copy_v3_v3(v3, l[j][2]->v->co); + } + + add_v3_v3(vmid, v1); + add_v3_v3(vmid, v2); + add_v3_v3(vmid, v3); + n += 3; + + if (do_global) { + mul_mat3_m4_v3(ob->obmat, v1); + mul_mat3_m4_v3(ob->obmat, v2); + mul_mat3_m4_v3(ob->obmat, v3); + } + + area += area_tri_v3(v1, v2, v3); + } + + mul_v3_fl(vmid, 1.0f / (float)n); + mul_m4_v3(ob->obmat, vmid); + + if (unit->system) { + numstr_len = BKE_unit_value_as_string( + numstr, + sizeof(numstr), + (double)(area * unit->scale_length * unit->scale_length), + 3, + B_UNIT_AREA, + unit, + false); + } + else { + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), conv_float, area); + } + + DRW_text_cache_add(dt, vmid, numstr, numstr_len, 0, 0, txt_flag, col); + } + looptri_index += f_looptri_len; + } + } + + if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_ANG) { + BMFace *efa; + const bool is_rad = (unit->system_rotation == USER_UNIT_ROT_RADIANS); + + UI_GetThemeColor3ubv(TH_DRAWEXTRA_FACEANG, col); + + if (use_coords) { + BM_mesh_elem_index_ensure(em->bm, BM_VERT); + } + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + const bool is_face_sel = BM_elem_flag_test_bool(efa, BM_ELEM_SELECT); + + if (is_face_sel || do_moving) { + BMIter liter; + BMLoop *loop; + bool is_first = true; + + BM_ITER_ELEM (loop, &liter, efa, BM_LOOPS_OF_FACE) { + if (is_face_sel || (do_moving && (BM_elem_flag_test(loop->v, BM_ELEM_SELECT) || + BM_elem_flag_test(loop->prev->v, BM_ELEM_SELECT) || + BM_elem_flag_test(loop->next->v, BM_ELEM_SELECT)))) { + float v2_local[3]; + + /* lazy init center calc */ + if (is_first) { + if (use_coords) { + BM_face_calc_center_bounds_vcos(em->bm, efa, vmid, vert_coords); + } + else { + BM_face_calc_center_bounds(efa, vmid); + } + is_first = false; + } + if (use_coords) { + copy_v3_v3(v1, vert_coords[BM_elem_index_get(loop->prev->v)]); + copy_v3_v3(v2, vert_coords[BM_elem_index_get(loop->v)]); + copy_v3_v3(v3, vert_coords[BM_elem_index_get(loop->next->v)]); + } + else { + copy_v3_v3(v1, loop->prev->v->co); + copy_v3_v3(v2, loop->v->co); + copy_v3_v3(v3, loop->next->v->co); + } + + copy_v3_v3(v2_local, v2); + + if (do_global) { + mul_mat3_m4_v3(ob->obmat, v1); + mul_mat3_m4_v3(ob->obmat, v2); + mul_mat3_m4_v3(ob->obmat, v3); + } + + float angle = angle_v3v3v3(v1, v2, v3); + + numstr_len = BLI_snprintf_rlen(numstr, + sizeof(numstr), + "%.3f%s", + (is_rad) ? angle : RAD2DEGF(angle), + (is_rad) ? "r" : "°"); + interp_v3_v3v3(fvec, vmid, v2_local, 0.8f); + mul_m4_v3(ob->obmat, fvec); + DRW_text_cache_add(dt, fvec, numstr, numstr_len, 0, 0, txt_flag, col); + } + } + } + } + } + + /* This option is for mesh ops and addons debugging; only available in UI if Blender starts with + * --debug */ + if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_INDICES) { + int i; + + /* For now, reuse an appropriate theme color */ + UI_GetThemeColor3ubv(TH_DRAWEXTRA_FACEANG, col); + + if (em->selectmode & SCE_SELECT_VERTEX) { + BMVert *v; + + if (use_coords) { + BM_mesh_elem_index_ensure(em->bm, BM_VERT); + } + BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, i) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + if (use_coords) { + copy_v3_v3(v1, vert_coords[BM_elem_index_get(v)]); + } + else { + copy_v3_v3(v1, v->co); + } + + mul_m4_v3(ob->obmat, v1); + + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", i); + DRW_text_cache_add(dt, v1, numstr, numstr_len, 0, 0, txt_flag, col); + } + } + } + + if (em->selectmode & SCE_SELECT_EDGE) { + BMEdge *eed; + + const bool use_edge_tex_sep = (edge_tex_count == 2); + const bool use_edge_tex_len = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN); + + BM_ITER_MESH_INDEX (eed, &iter, em->bm, BM_EDGES_OF_MESH, i) { + if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + float v1_clip[3], v2_clip[3]; + + if (use_coords) { + copy_v3_v3(v1, vert_coords[BM_elem_index_get(eed->v1)]); + copy_v3_v3(v2, vert_coords[BM_elem_index_get(eed->v2)]); + } + else { + copy_v3_v3(v1, eed->v1->co); + copy_v3_v3(v2, eed->v2->co); + } + + if (clip_segment_v3_plane_n(v1, v2, clip_planes, 4, v1_clip, v2_clip)) { + mid_v3_v3v3(vmid, v1_clip, v2_clip); + mul_m4_v3(ob->obmat, vmid); + + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", i); + DRW_text_cache_add( + dt, + vmid, + numstr, + numstr_len, + 0, + (use_edge_tex_sep) ? (use_edge_tex_len) ? -edge_tex_sep : edge_tex_sep : 0, + txt_flag, + col); + } + } + } + } + + if (em->selectmode & SCE_SELECT_FACE) { + BMFace *f; + + if (use_coords) { + BM_mesh_elem_index_ensure(em->bm, BM_VERT); + } + + BM_ITER_MESH_INDEX (f, &iter, em->bm, BM_FACES_OF_MESH, i) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + + if (use_coords) { + BM_face_calc_center_median_vcos(em->bm, f, v1, vert_coords); + } + else { + BM_face_calc_center_median(f, v1); + } + + mul_m4_v3(ob->obmat, v1); + + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", i); + DRW_text_cache_add(dt, v1, numstr, numstr_len, 0, 0, txt_flag, col); + } + } + } + } +} diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index a4857c2b92f..7c7b5771a04 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -1311,7 +1311,7 @@ void ED_view3d_draw_bgpic_test(const struct Scene *scene, bool do_foreground, bool do_camera_frame); -/* view3d_gizmo_preselect_type.c */ +/* view3d_gizmo_preselect_type.cc */ void ED_view3d_gizmo_mesh_preselect_get_active(struct bContext *C, struct wmGizmo *gz, diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index eaac33fda26..97e39c00c48 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -42,7 +42,7 @@ set(SRC view3d_gizmo_navigate.c view3d_gizmo_navigate_type.c view3d_gizmo_preselect.c - view3d_gizmo_preselect_type.c + view3d_gizmo_preselect_type.cc view3d_gizmo_ruler.c view3d_gizmo_tool_generic.c view3d_header.c diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c deleted file mode 100644 index 73617c0670f..00000000000 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ /dev/null @@ -1,572 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup wm - * - * \name Preselection Gizmo - * - * Use for tools to hover over data before activation. - * - * \note This is a slight misuse of gizmo's, since clicking performs no action. - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_math.h" - -#include "DNA_mesh_types.h" -#include "DNA_view3d_types.h" - -#include "BKE_context.h" -#include "BKE_editmesh.h" -#include "BKE_global.h" -#include "BKE_layer.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "bmesh.h" - -#include "ED_gizmo_library.h" -#include "ED_mesh.h" -#include "ED_screen.h" -#include "ED_view3d.h" - -/* -------------------------------------------------------------------- */ -/** \name Shared Internal API - * \{ */ - -/** - * Check if drawing should be performed, clear the pre-selection in the case it's disabled. - * Without this, the gizmo would be visible while transforming. See T92954. - * - * NOTE(@campbellbarton): This is a workaround for the gizmo system, since typically poll - * would be used for this purpose. The problem with using poll is once the gizmo is visible again - * is there is a visible flicker showing the previous location before cursor motion causes the - * pre selection to be updated. While this is only a glitch, it's distracting. - * The gizmo system it's self could support this use case by tracking which gizmos draw and ensure - * gizmos always run #wmGizmoType.test_select before drawing, however pre-selection is already - * outside the scope of what gizmos are meant to be used for, so keep this workaround localized - * to this gizmo type unless this seems worth supporting for more typical use-cases. - * - * Longer term it may be better to use #wmPaintCursor instead of gizmos (as snapping preview does). - */ -static bool gizmo_preselect_poll_for_draw(const bContext *C, wmGizmo *gz) -{ - if (G.moving == false) { - RegionView3D *rv3d = CTX_wm_region_view3d(C); - if (!(rv3d && (rv3d->rflag & RV3D_NAVIGATING))) { - return true; - } - } - ED_view3d_gizmo_mesh_preselect_clear(gz); - return false; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Mesh Element (Vert/Edge/Face) Pre-Select Gizmo API - * \{ */ - -typedef struct MeshElemGizmo3D { - wmGizmo gizmo; - Base **bases; - uint bases_len; - int base_index; - int vert_index; - int edge_index; - int face_index; - struct EditMesh_PreSelElem *psel; -} MeshElemGizmo3D; - -static void gizmo_preselect_elem_draw(const bContext *C, wmGizmo *gz) -{ - if (!gizmo_preselect_poll_for_draw(C, gz)) { - return; - } - - MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; - if (gz_ele->base_index != -1) { - Object *ob = gz_ele->bases[gz_ele->base_index]->object; - EDBM_preselect_elem_draw(gz_ele->psel, ob->obmat); - } -} - -static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2]) -{ - wmEvent *event = CTX_wm_window(C)->eventstate; - MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; - - /* Hack: Switch action mode based on key input */ - const bool is_ctrl_pressed = (event->modifier & KM_CTRL) != 0; - const bool is_shift_pressed = (event->modifier & KM_SHIFT) != 0; - EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM); - if (is_ctrl_pressed && !is_shift_pressed) { - EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE); - } - if (!is_ctrl_pressed && is_shift_pressed) { - EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_DELETE); - } - - struct { - Object *ob; - BMElem *ele; - float dist; - int base_index; - } best = { - .dist = ED_view3d_select_dist_px(), - }; - - { - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - BKE_view_layer_synced_ensure(scene, view_layer); - if ((gz_ele->bases) == NULL || - (gz_ele->bases[0] != BKE_view_layer_active_base_get(view_layer))) { - MEM_SAFE_FREE(gz_ele->bases); - gz_ele->bases = BKE_view_layer_array_from_bases_in_edit_mode( - scene, view_layer, v3d, &gz_ele->bases_len); - } - } - - ViewContext vc; - em_setup_viewcontext(C, &vc); - copy_v2_v2_int(vc.mval, mval); - - { - /* TODO: support faces. */ - int base_index_vert = -1; - int base_index_edge = -1; - int base_index_face = -1; - BMVert *eve_test; - BMEdge *eed_test; - BMFace *efa_test; - - if (EDBM_unified_findnearest_from_raycast(&vc, - gz_ele->bases, - gz_ele->bases_len, - false, - true, - &base_index_vert, - &base_index_edge, - &base_index_face, - &eve_test, - &eed_test, - &efa_test)) { - if (EDBM_preselect_action_get(gz_ele->psel) == PRESELECT_ACTION_DELETE) { - /* Delete action */ - if (efa_test) { - best.ele = (BMElem *)efa_test; - best.base_index = base_index_face; - } - } - - else { - /* Transform and create action */ - if (eed_test) { - best.ele = (BMElem *)eed_test; - best.base_index = base_index_edge; - } - } - - /* All actions use same vertex pre-selection. */ - /* Re-topology should always prioritize edge pre-selection. - * Only pre-select a vertex when the cursor is really close to it. */ - if (eve_test) { - BMVert *vert = (BMVert *)eve_test; - float vert_p_co[2], vert_co[3]; - const float mval_f[2] = {UNPACK2(vc.mval)}; - mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co); - ED_view3d_project_v2(vc.region, vert_co, vert_p_co); - float len = len_v2v2(vert_p_co, mval_f); - if (len < 35) { - best.ele = (BMElem *)eve_test; - best.base_index = base_index_vert; - } - if (!BM_vert_is_boundary(vert) && - EDBM_preselect_action_get(gz_ele->psel) != PRESELECT_ACTION_DELETE) { - best.ele = (BMElem *)eve_test; - best.base_index = base_index_vert; - } - } - - /* Check above should never fail, if it does it's an internal error. */ - BLI_assert(best.base_index != -1); - - Base *base = gz_ele->bases[best.base_index]; - best.ob = base->object; - } - } - - BMesh *bm = NULL; - - gz_ele->base_index = -1; - gz_ele->vert_index = -1; - gz_ele->edge_index = -1; - gz_ele->face_index = -1; - - if (best.ele) { - gz_ele->base_index = best.base_index; - bm = BKE_editmesh_from_object(gz_ele->bases[gz_ele->base_index]->object)->bm; - BM_mesh_elem_index_ensure(bm, best.ele->head.htype); - - if (best.ele->head.htype == BM_VERT) { - gz_ele->vert_index = BM_elem_index_get(best.ele); - } - else if (best.ele->head.htype == BM_EDGE) { - gz_ele->edge_index = BM_elem_index_get(best.ele); - } - else if (best.ele->head.htype == BM_FACE) { - gz_ele->face_index = BM_elem_index_get(best.ele); - } - } - - if (best.ele) { - const float(*coords)[3] = NULL; - { - Object *ob = gz_ele->bases[gz_ele->base_index]->object; - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data); - if (me_eval->runtime.edit_data) { - coords = me_eval->runtime.edit_data->vertexCos; - } - } - EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords); - EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval); - } - else { - EDBM_preselect_elem_clear(gz_ele->psel); - EDBM_preselect_preview_clear(gz_ele->psel); - } - - RNA_int_set(gz->ptr, "object_index", gz_ele->base_index); - RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index); - RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index); - RNA_int_set(gz->ptr, "face_index", gz_ele->face_index); - - if (best.ele) { - ARegion *region = CTX_wm_region(C); - ED_region_tag_redraw_editor_overlays(region); - } - - // return best.eed ? 0 : -1; - return -1; -} - -static void gizmo_preselect_elem_setup(wmGizmo *gz) -{ - MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; - if (gz_ele->psel == NULL) { - gz_ele->psel = EDBM_preselect_elem_create(); - } - gz_ele->base_index = -1; -} - -static void gizmo_preselect_elem_free(wmGizmo *gz) -{ - MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; - EDBM_preselect_elem_destroy(gz_ele->psel); - gz_ele->psel = NULL; - MEM_SAFE_FREE(gz_ele->bases); -} - -static int gizmo_preselect_elem_invoke(bContext *UNUSED(C), - wmGizmo *UNUSED(gz), - const wmEvent *UNUSED(event)) -{ - return OPERATOR_PASS_THROUGH; -} - -static void GIZMO_GT_mesh_preselect_elem_3d(wmGizmoType *gzt) -{ - /* identifiers */ - gzt->idname = "GIZMO_GT_mesh_preselect_elem_3d"; - - /* api callbacks */ - gzt->invoke = gizmo_preselect_elem_invoke; - gzt->draw = gizmo_preselect_elem_draw; - gzt->test_select = gizmo_preselect_elem_test_select; - gzt->setup = gizmo_preselect_elem_setup; - gzt->free = gizmo_preselect_elem_free; - - gzt->struct_size = sizeof(MeshElemGizmo3D); - - RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX); - RNA_def_int(gzt->srna, "vert_index", -1, -1, INT_MAX, "Vert Index", "", -1, INT_MAX); - RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX); - RNA_def_int(gzt->srna, "face_index", -1, -1, INT_MAX, "Face Index", "", -1, INT_MAX); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Mesh Edge-Ring Pre-Select Gizmo API - * \{ */ - -typedef struct MeshEdgeRingGizmo3D { - wmGizmo gizmo; - Base **bases; - uint bases_len; - int base_index; - int edge_index; - struct EditMesh_PreSelEdgeRing *psel; -} MeshEdgeRingGizmo3D; - -static void gizmo_preselect_edgering_draw(const bContext *C, wmGizmo *gz) -{ - if (!gizmo_preselect_poll_for_draw(C, gz)) { - return; - } - - MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz; - if (gz_ring->base_index != -1) { - Object *ob = gz_ring->bases[gz_ring->base_index]->object; - EDBM_preselect_edgering_draw(gz_ring->psel, ob->obmat); - } -} - -static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const int mval[2]) -{ - MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz; - struct { - Object *ob; - BMEdge *eed; - float dist; - int base_index; - } best = { - .dist = ED_view3d_select_dist_px(), - }; - - struct { - int base_index; - int edge_index; - } prev = { - .base_index = gz_ring->base_index, - .edge_index = gz_ring->edge_index, - }; - - { - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - View3D *v3d = CTX_wm_view3d(C); - BKE_view_layer_synced_ensure(scene, view_layer); - if ((gz_ring->bases) == NULL || - (gz_ring->bases[0] != BKE_view_layer_active_base_get(view_layer))) { - MEM_SAFE_FREE(gz_ring->bases); - gz_ring->bases = BKE_view_layer_array_from_bases_in_edit_mode( - scene, view_layer, v3d, &gz_ring->bases_len); - } - } - - ViewContext vc; - em_setup_viewcontext(C, &vc); - copy_v2_v2_int(vc.mval, mval); - - uint base_index; - BMEdge *eed_test = EDBM_edge_find_nearest_ex( - &vc, &best.dist, NULL, false, false, NULL, gz_ring->bases, gz_ring->bases_len, &base_index); - - if (eed_test) { - best.ob = gz_ring->bases[base_index]->object; - best.eed = eed_test; - best.base_index = base_index; - } - - BMesh *bm = NULL; - if (best.eed) { - gz_ring->base_index = best.base_index; - bm = BKE_editmesh_from_object(gz_ring->bases[gz_ring->base_index]->object)->bm; - BM_mesh_elem_index_ensure(bm, BM_EDGE); - gz_ring->edge_index = BM_elem_index_get(best.eed); - } - else { - gz_ring->base_index = -1; - gz_ring->edge_index = -1; - } - - if ((prev.base_index == gz_ring->base_index) && (prev.edge_index == gz_ring->edge_index)) { - /* pass (only recalculate on change) */ - } - else { - if (best.eed) { - Object *ob = gz_ring->bases[gz_ring->base_index]->object; - Scene *scene_eval = (Scene *)DEG_get_evaluated_id(vc.depsgraph, &vc.scene->id); - Object *ob_eval = DEG_get_evaluated_object(vc.depsgraph, ob); - BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval); - /* Re-allocate coords each update isn't ideal, however we can't be sure - * the mesh hasn't been edited since last update. */ - bool is_alloc = false; - const float(*coords)[3] = BKE_editmesh_vert_coords_when_deformed( - vc.depsgraph, em_eval, scene_eval, ob_eval, NULL, &is_alloc); - EDBM_preselect_edgering_update_from_edge(gz_ring->psel, bm, best.eed, 1, coords); - if (is_alloc) { - MEM_freeN((void *)coords); - } - } - else { - EDBM_preselect_edgering_clear(gz_ring->psel); - } - - RNA_int_set(gz->ptr, "object_index", gz_ring->base_index); - RNA_int_set(gz->ptr, "edge_index", gz_ring->edge_index); - - ARegion *region = CTX_wm_region(C); - ED_region_tag_redraw_editor_overlays(region); - } - - // return best.eed ? 0 : -1; - return -1; -} - -static void gizmo_preselect_edgering_setup(wmGizmo *gz) -{ - MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz; - if (gz_ring->psel == NULL) { - gz_ring->psel = EDBM_preselect_edgering_create(); - } - gz_ring->base_index = -1; -} - -static void gizmo_preselect_edgering_free(wmGizmo *gz) -{ - MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz; - EDBM_preselect_edgering_destroy(gz_ring->psel); - gz_ring->psel = NULL; - MEM_SAFE_FREE(gz_ring->bases); -} - -static int gizmo_preselect_edgering_invoke(bContext *UNUSED(C), - wmGizmo *UNUSED(gz), - const wmEvent *UNUSED(event)) -{ - return OPERATOR_PASS_THROUGH; -} - -static void GIZMO_GT_mesh_preselect_edgering_3d(wmGizmoType *gzt) -{ - /* identifiers */ - gzt->idname = "GIZMO_GT_mesh_preselect_edgering_3d"; - - /* api callbacks */ - gzt->invoke = gizmo_preselect_edgering_invoke; - gzt->draw = gizmo_preselect_edgering_draw; - gzt->test_select = gizmo_preselect_edgering_test_select; - gzt->setup = gizmo_preselect_edgering_setup; - gzt->free = gizmo_preselect_edgering_free; - - gzt->struct_size = sizeof(MeshEdgeRingGizmo3D); - - RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX); - RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Gizmo API - * \{ */ - -void ED_gizmotypes_preselect_3d(void) -{ - WM_gizmotype_append(GIZMO_GT_mesh_preselect_elem_3d); - WM_gizmotype_append(GIZMO_GT_mesh_preselect_edgering_3d); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Gizmo Accessors - * - * This avoids each user of the gizmo needing to write their own lookups to access - * the information from this gizmo. - * \{ */ - -void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C, - wmGizmo *gz, - Base **r_base, - BMElem **r_ele) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - - const int object_index = RNA_int_get(gz->ptr, "object_index"); - - /* weak, allocate an array just to access the index. */ - Base *base = NULL; - Object *obedit = NULL; - { - uint bases_len; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode( - scene, view_layer, CTX_wm_view3d(C), &bases_len); - if (object_index < bases_len) { - base = bases[object_index]; - obedit = base->object; - } - MEM_freeN(bases); - } - - *r_base = base; - *r_ele = NULL; - - if (obedit) { - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - PropertyRNA *prop; - - /* Ring select only defines edge, check properties exist first. */ - prop = RNA_struct_find_property(gz->ptr, "vert_index"); - const int vert_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1; - prop = RNA_struct_find_property(gz->ptr, "edge_index"); - const int edge_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1; - prop = RNA_struct_find_property(gz->ptr, "face_index"); - const int face_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1; - - if (vert_index != -1) { - *r_ele = (BMElem *)BM_vert_at_index_find(bm, vert_index); - } - else if (edge_index != -1) { - *r_ele = (BMElem *)BM_edge_at_index_find(bm, edge_index); - } - else if (face_index != -1) { - *r_ele = (BMElem *)BM_face_at_index_find(bm, face_index); - } - } -} - -void ED_view3d_gizmo_mesh_preselect_clear(wmGizmo *gz) -{ - if (STREQ(gz->type->idname, "GIZMO_GT_mesh_preselect_elem_3d")) { - MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; - gz_ele->base_index = -1; - gz_ele->vert_index = -1; - gz_ele->edge_index = -1; - gz_ele->face_index = -1; - } - else if (STREQ(gz->type->idname, "GIZMO_GT_mesh_preselect_edgering_3d")) { - MeshEdgeRingGizmo3D *gz_ele = (MeshEdgeRingGizmo3D *)gz; - gz_ele->base_index = -1; - gz_ele->edge_index = -1; - } - else { - BLI_assert_unreachable(); - } - - const char *prop_ids[] = {"object_index", "vert_index", "edge_index", "face_index"}; - for (int i = 0; i < ARRAY_SIZE(prop_ids); i++) { - PropertyRNA *prop = RNA_struct_find_property(gz->ptr, prop_ids[i]); - if (prop == NULL) { - continue; - } - RNA_property_int_set(gz->ptr, prop, -1); - } -} - -/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.cc b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.cc new file mode 100644 index 00000000000..fefc5d6fa93 --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.cc @@ -0,0 +1,576 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup wm + * + * \name Preselection Gizmo + * + * Use for tools to hover over data before activation. + * + * \note This is a slight misuse of gizmo's, since clicking performs no action. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "DNA_mesh_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_context.h" +#include "BKE_editmesh.h" +#include "BKE_global.h" +#include "BKE_layer.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "bmesh.h" + +#include "ED_gizmo_library.h" +#include "ED_mesh.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +/* -------------------------------------------------------------------- */ +/** \name Shared Internal API + * \{ */ + +/** + * Check if drawing should be performed, clear the pre-selection in the case it's disabled. + * Without this, the gizmo would be visible while transforming. See T92954. + * + * NOTE(@campbellbarton): This is a workaround for the gizmo system, since typically poll + * would be used for this purpose. The problem with using poll is once the gizmo is visible again + * is there is a visible flicker showing the previous location before cursor motion causes the + * pre selection to be updated. While this is only a glitch, it's distracting. + * The gizmo system it's self could support this use case by tracking which gizmos draw and ensure + * gizmos always run #wmGizmoType.test_select before drawing, however pre-selection is already + * outside the scope of what gizmos are meant to be used for, so keep this workaround localized + * to this gizmo type unless this seems worth supporting for more typical use-cases. + * + * Longer term it may be better to use #wmPaintCursor instead of gizmos (as snapping preview does). + */ +static bool gizmo_preselect_poll_for_draw(const bContext *C, wmGizmo *gz) +{ + if (G.moving == false) { + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (!(rv3d && (rv3d->rflag & RV3D_NAVIGATING))) { + return true; + } + } + ED_view3d_gizmo_mesh_preselect_clear(gz); + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Element (Vert/Edge/Face) Pre-Select Gizmo API + * \{ */ + +struct MeshElemGizmo3D { + wmGizmo gizmo; + Base **bases; + uint bases_len; + int base_index; + int vert_index; + int edge_index; + int face_index; + EditMesh_PreSelElem *psel; +}; + +static void gizmo_preselect_elem_draw(const bContext *C, wmGizmo *gz) +{ + if (!gizmo_preselect_poll_for_draw(C, gz)) { + return; + } + + MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + if (gz_ele->base_index != -1) { + Object *ob = gz_ele->bases[gz_ele->base_index]->object; + EDBM_preselect_elem_draw(gz_ele->psel, ob->obmat); + } +} + +static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2]) +{ + wmEvent *event = CTX_wm_window(C)->eventstate; + MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + + /* Hack: Switch action mode based on key input */ + const bool is_ctrl_pressed = (event->modifier & KM_CTRL) != 0; + const bool is_shift_pressed = (event->modifier & KM_SHIFT) != 0; + EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM); + if (is_ctrl_pressed && !is_shift_pressed) { + EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE); + } + if (!is_ctrl_pressed && is_shift_pressed) { + EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_DELETE); + } + + struct Best { + Object *ob; + BMElem *ele; + float dist; + int base_index; + } best{}; + best.dist = ED_view3d_select_dist_px(); + + { + const Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + BKE_view_layer_synced_ensure(scene, view_layer); + if ((gz_ele->bases) == nullptr || + (gz_ele->bases[0] != BKE_view_layer_active_base_get(view_layer))) { + MEM_SAFE_FREE(gz_ele->bases); + gz_ele->bases = BKE_view_layer_array_from_bases_in_edit_mode( + scene, view_layer, v3d, &gz_ele->bases_len); + } + } + + ViewContext vc; + em_setup_viewcontext(C, &vc); + copy_v2_v2_int(vc.mval, mval); + + { + /* TODO: support faces. */ + int base_index_vert = -1; + int base_index_edge = -1; + int base_index_face = -1; + BMVert *eve_test; + BMEdge *eed_test; + BMFace *efa_test; + + if (EDBM_unified_findnearest_from_raycast(&vc, + gz_ele->bases, + gz_ele->bases_len, + false, + true, + &base_index_vert, + &base_index_edge, + &base_index_face, + &eve_test, + &eed_test, + &efa_test)) { + if (EDBM_preselect_action_get(gz_ele->psel) == PRESELECT_ACTION_DELETE) { + /* Delete action */ + if (efa_test) { + best.ele = (BMElem *)efa_test; + best.base_index = base_index_face; + } + } + + else { + /* Transform and create action */ + if (eed_test) { + best.ele = (BMElem *)eed_test; + best.base_index = base_index_edge; + } + } + + /* All actions use same vertex pre-selection. */ + /* Re-topology should always prioritize edge pre-selection. + * Only pre-select a vertex when the cursor is really close to it. */ + if (eve_test) { + BMVert *vert = (BMVert *)eve_test; + float vert_p_co[2], vert_co[3]; + const float mval_f[2] = {float(vc.mval[0]), float(vc.mval[1])}; + mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co); + ED_view3d_project_v2(vc.region, vert_co, vert_p_co); + float len = len_v2v2(vert_p_co, mval_f); + if (len < 35) { + best.ele = (BMElem *)eve_test; + best.base_index = base_index_vert; + } + if (!BM_vert_is_boundary(vert) && + EDBM_preselect_action_get(gz_ele->psel) != PRESELECT_ACTION_DELETE) { + best.ele = (BMElem *)eve_test; + best.base_index = base_index_vert; + } + } + + /* Check above should never fail, if it does it's an internal error. */ + BLI_assert(best.base_index != -1); + + Base *base = gz_ele->bases[best.base_index]; + best.ob = base->object; + } + } + + BMesh *bm = nullptr; + + gz_ele->base_index = -1; + gz_ele->vert_index = -1; + gz_ele->edge_index = -1; + gz_ele->face_index = -1; + + if (best.ele) { + gz_ele->base_index = best.base_index; + bm = BKE_editmesh_from_object(gz_ele->bases[gz_ele->base_index]->object)->bm; + BM_mesh_elem_index_ensure(bm, best.ele->head.htype); + + if (best.ele->head.htype == BM_VERT) { + gz_ele->vert_index = BM_elem_index_get(best.ele); + } + else if (best.ele->head.htype == BM_EDGE) { + gz_ele->edge_index = BM_elem_index_get(best.ele); + } + else if (best.ele->head.htype == BM_FACE) { + gz_ele->face_index = BM_elem_index_get(best.ele); + } + } + + if (best.ele) { + const float(*coords)[3] = nullptr; + { + Object *ob = gz_ele->bases[gz_ele->base_index]->object; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, static_cast(ob->data)); + if (me_eval->runtime.edit_data) { + coords = me_eval->runtime.edit_data->vertexCos; + } + } + EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords); + EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval); + } + else { + EDBM_preselect_elem_clear(gz_ele->psel); + EDBM_preselect_preview_clear(gz_ele->psel); + } + + RNA_int_set(gz->ptr, "object_index", gz_ele->base_index); + RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index); + RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index); + RNA_int_set(gz->ptr, "face_index", gz_ele->face_index); + + if (best.ele) { + ARegion *region = CTX_wm_region(C); + ED_region_tag_redraw_editor_overlays(region); + } + + // return best.eed ? 0 : -1; + return -1; +} + +static void gizmo_preselect_elem_setup(wmGizmo *gz) +{ + MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + if (gz_ele->psel == nullptr) { + gz_ele->psel = EDBM_preselect_elem_create(); + } + gz_ele->base_index = -1; +} + +static void gizmo_preselect_elem_free(wmGizmo *gz) +{ + MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + EDBM_preselect_elem_destroy(gz_ele->psel); + gz_ele->psel = nullptr; + MEM_SAFE_FREE(gz_ele->bases); +} + +static int gizmo_preselect_elem_invoke(bContext *UNUSED(C), + wmGizmo *UNUSED(gz), + const wmEvent *UNUSED(event)) +{ + return OPERATOR_PASS_THROUGH; +} + +static void GIZMO_GT_mesh_preselect_elem_3d(wmGizmoType *gzt) +{ + /* identifiers */ + gzt->idname = "GIZMO_GT_mesh_preselect_elem_3d"; + + /* api callbacks */ + gzt->invoke = gizmo_preselect_elem_invoke; + gzt->draw = gizmo_preselect_elem_draw; + gzt->test_select = gizmo_preselect_elem_test_select; + gzt->setup = gizmo_preselect_elem_setup; + gzt->free = gizmo_preselect_elem_free; + + gzt->struct_size = sizeof(MeshElemGizmo3D); + + RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX); + RNA_def_int(gzt->srna, "vert_index", -1, -1, INT_MAX, "Vert Index", "", -1, INT_MAX); + RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX); + RNA_def_int(gzt->srna, "face_index", -1, -1, INT_MAX, "Face Index", "", -1, INT_MAX); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Edge-Ring Pre-Select Gizmo API + * \{ */ + +struct MeshEdgeRingGizmo3D { + wmGizmo gizmo; + Base **bases; + uint bases_len; + int base_index; + int edge_index; + EditMesh_PreSelEdgeRing *psel; +}; + +static void gizmo_preselect_edgering_draw(const bContext *C, wmGizmo *gz) +{ + if (!gizmo_preselect_poll_for_draw(C, gz)) { + return; + } + + MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz; + if (gz_ring->base_index != -1) { + Object *ob = gz_ring->bases[gz_ring->base_index]->object; + EDBM_preselect_edgering_draw(gz_ring->psel, ob->obmat); + } +} + +static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const int mval[2]) +{ + MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz; + struct Best { + Object *ob; + BMEdge *eed; + float dist; + int base_index; + } best{}; + best.dist = ED_view3d_select_dist_px(); + + struct Prev { + int base_index; + int edge_index; + } prev{}; + prev.base_index = gz_ring->base_index; + prev.edge_index = gz_ring->edge_index; + + { + const Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + View3D *v3d = CTX_wm_view3d(C); + BKE_view_layer_synced_ensure(scene, view_layer); + if ((gz_ring->bases) == nullptr || + (gz_ring->bases[0] != BKE_view_layer_active_base_get(view_layer))) { + MEM_SAFE_FREE(gz_ring->bases); + gz_ring->bases = BKE_view_layer_array_from_bases_in_edit_mode( + scene, view_layer, v3d, &gz_ring->bases_len); + } + } + + ViewContext vc; + em_setup_viewcontext(C, &vc); + copy_v2_v2_int(vc.mval, mval); + + uint base_index; + BMEdge *eed_test = EDBM_edge_find_nearest_ex(&vc, + &best.dist, + nullptr, + false, + false, + nullptr, + gz_ring->bases, + gz_ring->bases_len, + &base_index); + + if (eed_test) { + best.ob = gz_ring->bases[base_index]->object; + best.eed = eed_test; + best.base_index = base_index; + } + + BMesh *bm = nullptr; + if (best.eed) { + gz_ring->base_index = best.base_index; + bm = BKE_editmesh_from_object(gz_ring->bases[gz_ring->base_index]->object)->bm; + BM_mesh_elem_index_ensure(bm, BM_EDGE); + gz_ring->edge_index = BM_elem_index_get(best.eed); + } + else { + gz_ring->base_index = -1; + gz_ring->edge_index = -1; + } + + if ((prev.base_index == gz_ring->base_index) && (prev.edge_index == gz_ring->edge_index)) { + /* pass (only recalculate on change) */ + } + else { + if (best.eed) { + Object *ob = gz_ring->bases[gz_ring->base_index]->object; + Scene *scene_eval = (Scene *)DEG_get_evaluated_id(vc.depsgraph, &vc.scene->id); + Object *ob_eval = DEG_get_evaluated_object(vc.depsgraph, ob); + BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval); + /* Re-allocate coords each update isn't ideal, however we can't be sure + * the mesh hasn't been edited since last update. */ + bool is_alloc = false; + const float(*coords)[3] = BKE_editmesh_vert_coords_when_deformed( + vc.depsgraph, em_eval, scene_eval, ob_eval, nullptr, &is_alloc); + EDBM_preselect_edgering_update_from_edge(gz_ring->psel, bm, best.eed, 1, coords); + if (is_alloc) { + MEM_freeN((void *)coords); + } + } + else { + EDBM_preselect_edgering_clear(gz_ring->psel); + } + + RNA_int_set(gz->ptr, "object_index", gz_ring->base_index); + RNA_int_set(gz->ptr, "edge_index", gz_ring->edge_index); + + ARegion *region = CTX_wm_region(C); + ED_region_tag_redraw_editor_overlays(region); + } + + // return best.eed ? 0 : -1; + return -1; +} + +static void gizmo_preselect_edgering_setup(wmGizmo *gz) +{ + MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz; + if (gz_ring->psel == nullptr) { + gz_ring->psel = EDBM_preselect_edgering_create(); + } + gz_ring->base_index = -1; +} + +static void gizmo_preselect_edgering_free(wmGizmo *gz) +{ + MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz; + EDBM_preselect_edgering_destroy(gz_ring->psel); + gz_ring->psel = nullptr; + MEM_SAFE_FREE(gz_ring->bases); +} + +static int gizmo_preselect_edgering_invoke(bContext *UNUSED(C), + wmGizmo *UNUSED(gz), + const wmEvent *UNUSED(event)) +{ + return OPERATOR_PASS_THROUGH; +} + +static void GIZMO_GT_mesh_preselect_edgering_3d(wmGizmoType *gzt) +{ + /* identifiers */ + gzt->idname = "GIZMO_GT_mesh_preselect_edgering_3d"; + + /* api callbacks */ + gzt->invoke = gizmo_preselect_edgering_invoke; + gzt->draw = gizmo_preselect_edgering_draw; + gzt->test_select = gizmo_preselect_edgering_test_select; + gzt->setup = gizmo_preselect_edgering_setup; + gzt->free = gizmo_preselect_edgering_free; + + gzt->struct_size = sizeof(MeshEdgeRingGizmo3D); + + RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX); + RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gizmo API + * \{ */ + +void ED_gizmotypes_preselect_3d(void) +{ + WM_gizmotype_append(GIZMO_GT_mesh_preselect_elem_3d); + WM_gizmotype_append(GIZMO_GT_mesh_preselect_edgering_3d); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gizmo Accessors + * + * This avoids each user of the gizmo needing to write their own lookups to access + * the information from this gizmo. + * \{ */ + +void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C, + wmGizmo *gz, + Base **r_base, + BMElem **r_ele) +{ + const Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + const int object_index = RNA_int_get(gz->ptr, "object_index"); + + /* weak, allocate an array just to access the index. */ + Base *base = nullptr; + Object *obedit = nullptr; + { + uint bases_len; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode( + scene, view_layer, CTX_wm_view3d(C), &bases_len); + if (object_index < bases_len) { + base = bases[object_index]; + obedit = base->object; + } + MEM_freeN(bases); + } + + *r_base = base; + *r_ele = nullptr; + + if (obedit) { + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + PropertyRNA *prop; + + /* Ring select only defines edge, check properties exist first. */ + prop = RNA_struct_find_property(gz->ptr, "vert_index"); + const int vert_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1; + prop = RNA_struct_find_property(gz->ptr, "edge_index"); + const int edge_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1; + prop = RNA_struct_find_property(gz->ptr, "face_index"); + const int face_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1; + + if (vert_index != -1) { + *r_ele = (BMElem *)BM_vert_at_index_find(bm, vert_index); + } + else if (edge_index != -1) { + *r_ele = (BMElem *)BM_edge_at_index_find(bm, edge_index); + } + else if (face_index != -1) { + *r_ele = (BMElem *)BM_face_at_index_find(bm, face_index); + } + } +} + +void ED_view3d_gizmo_mesh_preselect_clear(wmGizmo *gz) +{ + if (STREQ(gz->type->idname, "GIZMO_GT_mesh_preselect_elem_3d")) { + MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; + gz_ele->base_index = -1; + gz_ele->vert_index = -1; + gz_ele->edge_index = -1; + gz_ele->face_index = -1; + } + else if (STREQ(gz->type->idname, "GIZMO_GT_mesh_preselect_edgering_3d")) { + MeshEdgeRingGizmo3D *gz_ele = (MeshEdgeRingGizmo3D *)gz; + gz_ele->base_index = -1; + gz_ele->edge_index = -1; + } + else { + BLI_assert_unreachable(); + } + + const char *prop_ids[] = {"object_index", "vert_index", "edge_index", "face_index"}; + for (int i = 0; i < ARRAY_SIZE(prop_ids); i++) { + PropertyRNA *prop = RNA_struct_find_property(gz->ptr, prop_ids[i]); + if (prop == nullptr) { + continue; + } + RNA_property_int_set(gz->ptr, prop, -1); + } +} + +/** \} */ diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 8bace2e048c..a982a181ed7 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -92,7 +92,7 @@ set(SRC intern/MOD_volume_displace.cc intern/MOD_volume_to_mesh.cc intern/MOD_warp.c - intern/MOD_wave.c + intern/MOD_wave.cc intern/MOD_weighted_normal.c intern/MOD_weightvg_util.c intern/MOD_weightvgedit.c diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c deleted file mode 100644 index b49a47b0fb4..00000000000 --- a/source/blender/modifiers/intern/MOD_wave.c +++ /dev/null @@ -1,495 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup modifiers - */ - -#include "BLI_utildefines.h" - -#include "BLI_math.h" - -#include "BLT_translation.h" - -#include "DNA_defaults.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" - -#include "BKE_context.h" -#include "BKE_deform.h" -#include "BKE_editmesh.h" -#include "BKE_lib_id.h" -#include "BKE_lib_query.h" -#include "BKE_mesh.h" -#include "BKE_mesh_wrapper.h" -#include "BKE_scene.h" -#include "BKE_screen.h" -#include "BKE_texture.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "RNA_access.h" -#include "RNA_prototypes.h" - -#include "MEM_guardedalloc.h" - -#include "RE_texture.h" - -#include "MOD_modifiertypes.h" -#include "MOD_ui_common.h" -#include "MOD_util.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -static void initData(ModifierData *md) -{ - WaveModifierData *wmd = (WaveModifierData *)md; - - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier)); - - MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WaveModifierData), modifier); -} - -static bool dependsOnTime(struct Scene *UNUSED(scene), ModifierData *UNUSED(md)) -{ - return true; -} - -static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) -{ - WaveModifierData *wmd = (WaveModifierData *)md; - - walk(userData, ob, (ID **)&wmd->texture, IDWALK_CB_USER); - walk(userData, ob, (ID **)&wmd->objectcenter, IDWALK_CB_NOP); - walk(userData, ob, (ID **)&wmd->map_object, IDWALK_CB_NOP); -} - -static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData) -{ - walk(userData, ob, md, "texture"); -} - -static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) -{ - WaveModifierData *wmd = (WaveModifierData *)md; - bool need_transform_relation = false; - - if (wmd->objectcenter != NULL) { - DEG_add_object_relation(ctx->node, wmd->objectcenter, DEG_OB_COMP_TRANSFORM, "Wave Modifier"); - need_transform_relation = true; - } - - if (wmd->texture != NULL) { - DEG_add_generic_id_relation(ctx->node, &wmd->texture->id, "Wave Modifier"); - - if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != NULL) { - MOD_depsgraph_update_object_bone_relation( - ctx->node, wmd->map_object, wmd->map_bone, "Wave Modifier"); - need_transform_relation = true; - } - else if (wmd->texmapping == MOD_DISP_MAP_GLOBAL) { - need_transform_relation = true; - } - } - - if (need_transform_relation) { - DEG_add_depends_on_transform_relation(ctx->node, "Wave Modifier"); - } -} - -static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) -{ - WaveModifierData *wmd = (WaveModifierData *)md; - - /* ask for UV coordinates if we need them */ - if (wmd->texture && wmd->texmapping == MOD_DISP_MAP_UV) { - r_cddata_masks->fmask |= CD_MASK_MTFACE; - } - - /* ask for vertexgroups if we need them */ - if (wmd->defgrp_name[0] != '\0') { - r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; - } -} - -static bool dependsOnNormals(ModifierData *md) -{ - WaveModifierData *wmd = (WaveModifierData *)md; - - return (wmd->flag & MOD_WAVE_NORM) != 0; -} - -static void waveModifier_do(WaveModifierData *md, - const ModifierEvalContext *ctx, - Object *ob, - Mesh *mesh, - float (*vertexCos)[3], - int verts_num) -{ - WaveModifierData *wmd = (WaveModifierData *)md; - const MDeformVert *dvert; - int defgrp_index; - float ctime = DEG_get_ctime(ctx->depsgraph); - float minfac = (float)(1.0 / exp(wmd->width * wmd->narrow * wmd->width * wmd->narrow)); - float lifefac = wmd->height; - float(*tex_co)[3] = NULL; - const int wmd_axis = wmd->flag & (MOD_WAVE_X | MOD_WAVE_Y); - const float falloff = wmd->falloff; - float falloff_fac = 1.0f; /* when falloff == 0.0f this stays at 1.0f */ - const bool invert_group = (wmd->flag & MOD_WAVE_INVERT_VGROUP) != 0; - - const float(*vert_normals)[3] = NULL; - if ((wmd->flag & MOD_WAVE_NORM) && (mesh != NULL)) { - vert_normals = BKE_mesh_vertex_normals_ensure(mesh); - } - - if (wmd->objectcenter != NULL) { - float mat[4][4]; - /* get the control object's location in local coordinates */ - invert_m4_m4(ob->imat, ob->obmat); - mul_m4_m4m4(mat, ob->imat, wmd->objectcenter->obmat); - - wmd->startx = mat[3][0]; - wmd->starty = mat[3][1]; - } - - /* get the index of the deform group */ - MOD_get_vgroup(ob, mesh, wmd->defgrp_name, &dvert, &defgrp_index); - - if (wmd->damp == 0.0f) { - wmd->damp = 10.0f; - } - - if (wmd->lifetime != 0.0f) { - float x = ctime - wmd->timeoffs; - - if (x > wmd->lifetime) { - lifefac = x - wmd->lifetime; - - if (lifefac > wmd->damp) { - lifefac = 0.0; - } - else { - lifefac = (float)(wmd->height * (1.0f - sqrtf(lifefac / wmd->damp))); - } - } - } - - Tex *tex_target = wmd->texture; - if (mesh != NULL && tex_target != NULL) { - tex_co = MEM_malloc_arrayN(verts_num, sizeof(*tex_co), "waveModifier_do tex_co"); - MOD_get_texture_coords((MappingInfoModifierData *)wmd, ctx, ob, mesh, vertexCos, tex_co); - - MOD_init_texture((MappingInfoModifierData *)wmd, ctx); - } - - if (lifefac != 0.0f) { - /* avoid divide by zero checks within the loop */ - float falloff_inv = falloff != 0.0f ? 1.0f / falloff : 1.0f; - int i; - - for (i = 0; i < verts_num; i++) { - float *co = vertexCos[i]; - float x = co[0] - wmd->startx; - float y = co[1] - wmd->starty; - float amplit = 0.0f; - float def_weight = 1.0f; - - /* get weights */ - if (dvert) { - def_weight = invert_group ? 1.0f - BKE_defvert_find_weight(&dvert[i], defgrp_index) : - BKE_defvert_find_weight(&dvert[i], defgrp_index); - - /* if this vert isn't in the vgroup, don't deform it */ - if (def_weight == 0.0f) { - continue; - } - } - - switch (wmd_axis) { - case MOD_WAVE_X | MOD_WAVE_Y: - amplit = sqrtf(x * x + y * y); - break; - case MOD_WAVE_X: - amplit = x; - break; - case MOD_WAVE_Y: - amplit = y; - break; - } - - /* this way it makes nice circles */ - amplit -= (ctime - wmd->timeoffs) * wmd->speed; - - if (wmd->flag & MOD_WAVE_CYCL) { - amplit = (float)fmodf(amplit - wmd->width, 2.0f * wmd->width) + wmd->width; - } - - if (falloff != 0.0f) { - float dist = 0.0f; - - switch (wmd_axis) { - case MOD_WAVE_X | MOD_WAVE_Y: - dist = sqrtf(x * x + y * y); - break; - case MOD_WAVE_X: - dist = fabsf(x); - break; - case MOD_WAVE_Y: - dist = fabsf(y); - break; - } - - falloff_fac = (1.0f - (dist * falloff_inv)); - CLAMP(falloff_fac, 0.0f, 1.0f); - } - - /* GAUSSIAN */ - if ((falloff_fac != 0.0f) && (amplit > -wmd->width) && (amplit < wmd->width)) { - amplit = amplit * wmd->narrow; - amplit = (float)(1.0f / expf(amplit * amplit) - minfac); - - /* Apply texture. */ - if (tex_co) { - Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - TexResult texres; - BKE_texture_get_value(scene, tex_target, tex_co[i], &texres, false); - amplit *= texres.tin; - } - - /* Apply weight & falloff. */ - amplit *= def_weight * falloff_fac; - - if (vert_normals) { - /* move along normals */ - if (wmd->flag & MOD_WAVE_NORM_X) { - co[0] += (lifefac * amplit) * vert_normals[i][0]; - } - if (wmd->flag & MOD_WAVE_NORM_Y) { - co[1] += (lifefac * amplit) * vert_normals[i][1]; - } - if (wmd->flag & MOD_WAVE_NORM_Z) { - co[2] += (lifefac * amplit) * vert_normals[i][2]; - } - } - else { - /* move along local z axis */ - co[2] += lifefac * amplit; - } - } - } - } - - MEM_SAFE_FREE(tex_co); -} - -static void deformVerts(ModifierData *md, - const ModifierEvalContext *ctx, - Mesh *mesh, - float (*vertexCos)[3], - int verts_num) -{ - WaveModifierData *wmd = (WaveModifierData *)md; - Mesh *mesh_src = NULL; - - if (wmd->flag & MOD_WAVE_NORM) { - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, vertexCos, verts_num, false); - } - else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') { - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); - } - - waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); - - if (!ELEM(mesh_src, NULL, mesh)) { - BKE_id_free(NULL, mesh_src); - } -} - -static void deformVertsEM(ModifierData *md, - const ModifierEvalContext *ctx, - struct BMEditMesh *editData, - Mesh *mesh, - float (*vertexCos)[3], - int verts_num) -{ - WaveModifierData *wmd = (WaveModifierData *)md; - Mesh *mesh_src = NULL; - - if (wmd->flag & MOD_WAVE_NORM) { - mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, vertexCos, verts_num, false); - } - else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') { - mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - } - - /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ - if (mesh_src != NULL) { - BKE_mesh_wrapper_ensure_mdata(mesh_src); - } - - waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); - - if (!ELEM(mesh_src, NULL, mesh)) { - /* Important not to free `vertexCos` owned by the caller. */ - EditMeshData *edit_data = mesh_src->runtime.edit_data; - if (edit_data->vertexCos == vertexCos) { - edit_data->vertexCos = NULL; - } - - BKE_id_free(NULL, mesh_src); - } -} - -static void panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *sub, *row, *col; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - uiLayoutSetPropSep(layout, true); - - row = uiLayoutRowWithHeading(layout, true, IFACE_("Motion")); - uiItemR(row, ptr, "use_x", UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE, NULL, ICON_NONE); - uiItemR(row, ptr, "use_y", UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE, NULL, ICON_NONE); - - uiItemR(layout, ptr, "use_cyclic", 0, NULL, ICON_NONE); - - row = uiLayoutRowWithHeading(layout, true, IFACE_("Along Normals")); - uiItemR(row, ptr, "use_normal", 0, "", ICON_NONE); - sub = uiLayoutRow(row, true); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_normal")); - uiItemR(sub, ptr, "use_normal_x", UI_ITEM_R_TOGGLE, "X", ICON_NONE); - uiItemR(sub, ptr, "use_normal_y", UI_ITEM_R_TOGGLE, "Y", ICON_NONE); - uiItemR(sub, ptr, "use_normal_z", UI_ITEM_R_TOGGLE, "Z", ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "falloff_radius", 0, IFACE_("Falloff"), ICON_NONE); - uiItemR(col, ptr, "height", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "width", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "narrowness", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); - - modifier_panel_end(layout, ptr); -} - -static void position_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *col; - uiLayout *layout = panel->layout; - - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - - uiLayoutSetPropSep(layout, true); - - uiItemR(layout, ptr, "start_position_object", 0, IFACE_("Object"), ICON_NONE); - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "start_position_x", 0, IFACE_("Start Position X"), ICON_NONE); - uiItemR(col, ptr, "start_position_y", 0, "Y", ICON_NONE); -} - -static void time_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *col; - uiLayout *layout = panel->layout; - - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - - uiLayoutSetPropSep(layout, true); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "time_offset", 0, IFACE_("Offset"), ICON_NONE); - uiItemR(col, ptr, "lifetime", 0, IFACE_("Life"), ICON_NONE); - uiItemR(col, ptr, "damping_time", 0, IFACE_("Damping"), ICON_NONE); - uiItemR(col, ptr, "speed", UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - -static void texture_panel_draw(const bContext *C, Panel *panel) -{ - uiLayout *col; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - int texture_coords = RNA_enum_get(ptr, "texture_coords"); - - uiTemplateID(layout, C, ptr, "texture", "texture.new", NULL, NULL, 0, ICON_NONE, NULL); - - uiLayoutSetPropSep(layout, true); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "texture_coords", 0, IFACE_("Coordinates"), ICON_NONE); - if (texture_coords == MOD_DISP_MAP_OBJECT) { - uiItemR(col, ptr, "texture_coords_object", 0, IFACE_("Object"), ICON_NONE); - PointerRNA texture_coords_obj_ptr = RNA_pointer_get(ptr, "texture_coords_object"); - if (!RNA_pointer_is_null(&texture_coords_obj_ptr) && - (RNA_enum_get(&texture_coords_obj_ptr, "type") == OB_ARMATURE)) { - PointerRNA texture_coords_obj_data_ptr = RNA_pointer_get(&texture_coords_obj_ptr, "data"); - uiItemPointerR(col, - ptr, - "texture_coords_bone", - &texture_coords_obj_data_ptr, - "bones", - IFACE_("Bone"), - ICON_NONE); - } - } - else if (texture_coords == MOD_DISP_MAP_UV && RNA_enum_get(&ob_ptr, "type") == OB_MESH) { - PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); - uiItemPointerR(col, ptr, "uv_layer", &obj_data_ptr, "uv_layers", NULL, ICON_NONE); - } -} - -static void panelRegister(ARegionType *region_type) -{ - PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Wave, panel_draw); - modifier_subpanel_register( - region_type, "position", "Start Position", NULL, position_panel_draw, panel_type); - modifier_subpanel_register(region_type, "time", "Time", NULL, time_panel_draw, panel_type); - modifier_subpanel_register( - region_type, "texture", "Texture", NULL, texture_panel_draw, panel_type); -} - -ModifierTypeInfo modifierType_Wave = { - /* name */ N_("Wave"), - /* structName */ "WaveModifierData", - /* structSize */ sizeof(WaveModifierData), - /* srna */ &RNA_WaveModifier, - /* type */ eModifierTypeType_OnlyDeform, - /* flags */ eModifierTypeFlag_AcceptsCVs | eModifierTypeFlag_AcceptsVertexCosOnly | - eModifierTypeFlag_SupportsEditmode, - /* icon */ ICON_MOD_WAVE, - - /* copyData */ BKE_modifier_copydata_generic, - - /* deformVerts */ deformVerts, - /* deformMatrices */ NULL, - /* deformVertsEM */ deformVertsEM, - /* deformMatricesEM */ NULL, - /* modifyMesh */ NULL, - /* modifyGeometrySet */ NULL, - - /* initData */ initData, - /* requiredDataMask */ requiredDataMask, - /* freeData */ NULL, - /* isDisabled */ NULL, - /* updateDepsgraph */ updateDepsgraph, - /* dependsOnTime */ dependsOnTime, - /* dependsOnNormals */ dependsOnNormals, - /* foreachIDLink */ foreachIDLink, - /* foreachTexLink */ foreachTexLink, - /* freeRuntimeData */ NULL, - /* panelRegister */ panelRegister, - /* blendWrite */ NULL, - /* blendRead */ NULL, -}; diff --git a/source/blender/modifiers/intern/MOD_wave.cc b/source/blender/modifiers/intern/MOD_wave.cc new file mode 100644 index 00000000000..44002b2d719 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_wave.cc @@ -0,0 +1,497 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup modifiers + */ + +#include "BLI_utildefines.h" + +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_defaults.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_editmesh.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_mesh.h" +#include "BKE_mesh_wrapper.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_texture.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "MEM_guardedalloc.h" + +#include "RE_texture.h" + +#include "MOD_modifiertypes.h" +#include "MOD_ui_common.h" +#include "MOD_util.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +static void initData(ModifierData *md) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier)); + + MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WaveModifierData), modifier); +} + +static bool dependsOnTime(Scene *UNUSED(scene), ModifierData *UNUSED(md)) +{ + return true; +} + +static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + + walk(userData, ob, (ID **)&wmd->texture, IDWALK_CB_USER); + walk(userData, ob, (ID **)&wmd->objectcenter, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&wmd->map_object, IDWALK_CB_NOP); +} + +static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData) +{ + walk(userData, ob, md, "texture"); +} + +static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + bool need_transform_relation = false; + + if (wmd->objectcenter != nullptr) { + DEG_add_object_relation(ctx->node, wmd->objectcenter, DEG_OB_COMP_TRANSFORM, "Wave Modifier"); + need_transform_relation = true; + } + + if (wmd->texture != nullptr) { + DEG_add_generic_id_relation(ctx->node, &wmd->texture->id, "Wave Modifier"); + + if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != nullptr) { + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->map_object, wmd->map_bone, "Wave Modifier"); + need_transform_relation = true; + } + else if (wmd->texmapping == MOD_DISP_MAP_GLOBAL) { + need_transform_relation = true; + } + } + + if (need_transform_relation) { + DEG_add_depends_on_transform_relation(ctx->node, "Wave Modifier"); + } +} + +static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + + /* ask for UV coordinates if we need them */ + if (wmd->texture && wmd->texmapping == MOD_DISP_MAP_UV) { + r_cddata_masks->fmask |= CD_MASK_MTFACE; + } + + /* ask for vertexgroups if we need them */ + if (wmd->defgrp_name[0] != '\0') { + r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; + } +} + +static bool dependsOnNormals(ModifierData *md) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + + return (wmd->flag & MOD_WAVE_NORM) != 0; +} + +static void waveModifier_do(WaveModifierData *md, + const ModifierEvalContext *ctx, + Object *ob, + Mesh *mesh, + float (*vertexCos)[3], + int verts_num) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + const MDeformVert *dvert; + int defgrp_index; + float ctime = DEG_get_ctime(ctx->depsgraph); + float minfac = (float)(1.0 / exp(wmd->width * wmd->narrow * wmd->width * wmd->narrow)); + float lifefac = wmd->height; + float(*tex_co)[3] = nullptr; + const int wmd_axis = wmd->flag & (MOD_WAVE_X | MOD_WAVE_Y); + const float falloff = wmd->falloff; + float falloff_fac = 1.0f; /* when falloff == 0.0f this stays at 1.0f */ + const bool invert_group = (wmd->flag & MOD_WAVE_INVERT_VGROUP) != 0; + + const float(*vert_normals)[3] = nullptr; + if ((wmd->flag & MOD_WAVE_NORM) && (mesh != nullptr)) { + vert_normals = BKE_mesh_vertex_normals_ensure(mesh); + } + + if (wmd->objectcenter != nullptr) { + float mat[4][4]; + /* get the control object's location in local coordinates */ + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_m4m4(mat, ob->imat, wmd->objectcenter->obmat); + + wmd->startx = mat[3][0]; + wmd->starty = mat[3][1]; + } + + /* get the index of the deform group */ + MOD_get_vgroup(ob, mesh, wmd->defgrp_name, &dvert, &defgrp_index); + + if (wmd->damp == 0.0f) { + wmd->damp = 10.0f; + } + + if (wmd->lifetime != 0.0f) { + float x = ctime - wmd->timeoffs; + + if (x > wmd->lifetime) { + lifefac = x - wmd->lifetime; + + if (lifefac > wmd->damp) { + lifefac = 0.0; + } + else { + lifefac = (float)(wmd->height * (1.0f - sqrtf(lifefac / wmd->damp))); + } + } + } + + Tex *tex_target = wmd->texture; + if (mesh != nullptr && tex_target != nullptr) { + tex_co = static_cast(MEM_malloc_arrayN(verts_num, sizeof(*tex_co), __func__)); + MOD_get_texture_coords((MappingInfoModifierData *)wmd, ctx, ob, mesh, vertexCos, tex_co); + + MOD_init_texture((MappingInfoModifierData *)wmd, ctx); + } + + if (lifefac != 0.0f) { + /* avoid divide by zero checks within the loop */ + float falloff_inv = falloff != 0.0f ? 1.0f / falloff : 1.0f; + int i; + + for (i = 0; i < verts_num; i++) { + float *co = vertexCos[i]; + float x = co[0] - wmd->startx; + float y = co[1] - wmd->starty; + float amplit = 0.0f; + float def_weight = 1.0f; + + /* get weights */ + if (dvert) { + def_weight = invert_group ? 1.0f - BKE_defvert_find_weight(&dvert[i], defgrp_index) : + BKE_defvert_find_weight(&dvert[i], defgrp_index); + + /* if this vert isn't in the vgroup, don't deform it */ + if (def_weight == 0.0f) { + continue; + } + } + + switch (wmd_axis) { + case MOD_WAVE_X | MOD_WAVE_Y: + amplit = sqrtf(x * x + y * y); + break; + case MOD_WAVE_X: + amplit = x; + break; + case MOD_WAVE_Y: + amplit = y; + break; + } + + /* this way it makes nice circles */ + amplit -= (ctime - wmd->timeoffs) * wmd->speed; + + if (wmd->flag & MOD_WAVE_CYCL) { + amplit = (float)fmodf(amplit - wmd->width, 2.0f * wmd->width) + wmd->width; + } + + if (falloff != 0.0f) { + float dist = 0.0f; + + switch (wmd_axis) { + case MOD_WAVE_X | MOD_WAVE_Y: + dist = sqrtf(x * x + y * y); + break; + case MOD_WAVE_X: + dist = fabsf(x); + break; + case MOD_WAVE_Y: + dist = fabsf(y); + break; + } + + falloff_fac = (1.0f - (dist * falloff_inv)); + CLAMP(falloff_fac, 0.0f, 1.0f); + } + + /* GAUSSIAN */ + if ((falloff_fac != 0.0f) && (amplit > -wmd->width) && (amplit < wmd->width)) { + amplit = amplit * wmd->narrow; + amplit = (float)(1.0f / expf(amplit * amplit) - minfac); + + /* Apply texture. */ + if (tex_co) { + Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + TexResult texres; + BKE_texture_get_value(scene, tex_target, tex_co[i], &texres, false); + amplit *= texres.tin; + } + + /* Apply weight & falloff. */ + amplit *= def_weight * falloff_fac; + + if (vert_normals) { + /* move along normals */ + if (wmd->flag & MOD_WAVE_NORM_X) { + co[0] += (lifefac * amplit) * vert_normals[i][0]; + } + if (wmd->flag & MOD_WAVE_NORM_Y) { + co[1] += (lifefac * amplit) * vert_normals[i][1]; + } + if (wmd->flag & MOD_WAVE_NORM_Z) { + co[2] += (lifefac * amplit) * vert_normals[i][2]; + } + } + else { + /* move along local z axis */ + co[2] += lifefac * amplit; + } + } + } + } + + MEM_SAFE_FREE(tex_co); +} + +static void deformVerts(ModifierData *md, + const ModifierEvalContext *ctx, + Mesh *mesh, + float (*vertexCos)[3], + int verts_num) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + Mesh *mesh_src = nullptr; + + if (wmd->flag & MOD_WAVE_NORM) { + mesh_src = MOD_deform_mesh_eval_get(ctx->object, nullptr, mesh, vertexCos, verts_num, false); + } + else if (wmd->texture != nullptr || wmd->defgrp_name[0] != '\0') { + mesh_src = MOD_deform_mesh_eval_get(ctx->object, nullptr, mesh, nullptr, verts_num, false); + } + + waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); + + if (!ELEM(mesh_src, nullptr, mesh)) { + BKE_id_free(nullptr, mesh_src); + } +} + +static void deformVertsEM(ModifierData *md, + const ModifierEvalContext *ctx, + BMEditMesh *editData, + Mesh *mesh, + float (*vertexCos)[3], + int verts_num) +{ + WaveModifierData *wmd = (WaveModifierData *)md; + Mesh *mesh_src = nullptr; + + if (wmd->flag & MOD_WAVE_NORM) { + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, vertexCos, verts_num, false); + } + else if (wmd->texture != nullptr || wmd->defgrp_name[0] != '\0') { + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, nullptr, verts_num, false); + } + + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ + if (mesh_src != nullptr) { + BKE_mesh_wrapper_ensure_mdata(mesh_src); + } + + waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); + + if (!ELEM(mesh_src, nullptr, mesh)) { + /* Important not to free `vertexCos` owned by the caller. */ + EditMeshData *edit_data = mesh_src->runtime.edit_data; + if (edit_data->vertexCos == vertexCos) { + edit_data->vertexCos = nullptr; + } + + BKE_id_free(nullptr, mesh_src); + } +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *sub, *row, *col; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + row = uiLayoutRowWithHeading(layout, true, IFACE_("Motion")); + uiItemR( + row, ptr, "use_x", UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE, nullptr, ICON_NONE); + uiItemR( + row, ptr, "use_y", UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "use_cyclic", 0, nullptr, ICON_NONE); + + row = uiLayoutRowWithHeading(layout, true, IFACE_("Along Normals")); + uiItemR(row, ptr, "use_normal", 0, "", ICON_NONE); + sub = uiLayoutRow(row, true); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_normal")); + uiItemR(sub, ptr, "use_normal_x", UI_ITEM_R_TOGGLE, "X", ICON_NONE); + uiItemR(sub, ptr, "use_normal_y", UI_ITEM_R_TOGGLE, "Y", ICON_NONE); + uiItemR(sub, ptr, "use_normal_z", UI_ITEM_R_TOGGLE, "Z", ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "falloff_radius", 0, IFACE_("Falloff"), ICON_NONE); + uiItemR(col, ptr, "height", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "width", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "narrowness", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr); + + modifier_panel_end(layout, ptr); +} + +static void position_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "start_position_object", 0, IFACE_("Object"), ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "start_position_x", 0, IFACE_("Start Position X"), ICON_NONE); + uiItemR(col, ptr, "start_position_y", 0, "Y", ICON_NONE); +} + +static void time_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + + uiLayoutSetPropSep(layout, true); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "time_offset", 0, IFACE_("Offset"), ICON_NONE); + uiItemR(col, ptr, "lifetime", 0, IFACE_("Life"), ICON_NONE); + uiItemR(col, ptr, "damping_time", 0, IFACE_("Damping"), ICON_NONE); + uiItemR(col, ptr, "speed", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void texture_panel_draw(const bContext *C, Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + int texture_coords = RNA_enum_get(ptr, "texture_coords"); + + uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); + + uiLayoutSetPropSep(layout, true); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "texture_coords", 0, IFACE_("Coordinates"), ICON_NONE); + if (texture_coords == MOD_DISP_MAP_OBJECT) { + uiItemR(col, ptr, "texture_coords_object", 0, IFACE_("Object"), ICON_NONE); + PointerRNA texture_coords_obj_ptr = RNA_pointer_get(ptr, "texture_coords_object"); + if (!RNA_pointer_is_null(&texture_coords_obj_ptr) && + (RNA_enum_get(&texture_coords_obj_ptr, "type") == OB_ARMATURE)) { + PointerRNA texture_coords_obj_data_ptr = RNA_pointer_get(&texture_coords_obj_ptr, "data"); + uiItemPointerR(col, + ptr, + "texture_coords_bone", + &texture_coords_obj_data_ptr, + "bones", + IFACE_("Bone"), + ICON_NONE); + } + } + else if (texture_coords == MOD_DISP_MAP_UV && RNA_enum_get(&ob_ptr, "type") == OB_MESH) { + PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); + uiItemPointerR(col, ptr, "uv_layer", &obj_data_ptr, "uv_layers", nullptr, ICON_NONE); + } +} + +static void panelRegister(ARegionType *region_type) +{ + PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Wave, panel_draw); + modifier_subpanel_register( + region_type, "position", "Start Position", nullptr, position_panel_draw, panel_type); + modifier_subpanel_register(region_type, "time", "Time", nullptr, time_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "texture", "Texture", nullptr, texture_panel_draw, panel_type); +} + +ModifierTypeInfo modifierType_Wave = { + /* name */ N_("Wave"), + /* structName */ "WaveModifierData", + /* structSize */ sizeof(WaveModifierData), + /* srna */ &RNA_WaveModifier, + /* type */ eModifierTypeType_OnlyDeform, + /* flags */ eModifierTypeFlag_AcceptsCVs | eModifierTypeFlag_AcceptsVertexCosOnly | + eModifierTypeFlag_SupportsEditmode, + /* icon */ ICON_MOD_WAVE, + + /* copyData */ BKE_modifier_copydata_generic, + + /* deformVerts */ deformVerts, + /* deformMatrices */ nullptr, + /* deformVertsEM */ deformVertsEM, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ nullptr, + /* modifyGeometrySet */ nullptr, + + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ nullptr, + /* isDisabled */ nullptr, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ dependsOnTime, + /* dependsOnNormals */ dependsOnNormals, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ foreachTexLink, + /* freeRuntimeData */ nullptr, + /* panelRegister */ panelRegister, + /* blendWrite */ nullptr, + /* blendRead */ nullptr, +}; -- cgit v1.2.3