diff options
Diffstat (limited to 'source/blender/blenkernel/intern/mesh_wrapper.cc')
-rw-r--r-- | source/blender/blenkernel/intern/mesh_wrapper.cc | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc new file mode 100644 index 00000000000..8291765c2ef --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -0,0 +1,380 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + * + * The primary purpose of this API is to avoid unnecessary mesh conversion for the final + * output of a modified mesh. + * + * This API handles the case when the modifier stack outputs a mesh which does not have + * #Mesh data (#MPoly, #MLoop, #MEdge, #MVert). + * Currently this is used so the resulting mesh can have #BMEditMesh data, + * postponing the converting until it's needed or avoiding conversion entirely + * which can be an expensive operation. + * Once converted, the meshes type changes to #ME_WRAPPER_TYPE_MDATA, + * although the edit mesh is not cleared. + * + * This API exposes functions that abstract over the different kinds of internal data, + * as well as supporting converting the mesh into regular mesh. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BLI_ghash.h" +#include "BLI_math.h" +#include "BLI_task.hh" +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_wrapper.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_subdiv.h" +#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_modifier.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em, + const CustomData_MeshMasks *cd_mask_extra, + const float (*vert_coords)[3], + const Mesh *me_settings) +{ + Mesh *me = static_cast<Mesh *>(BKE_id_new_nomain(ID_ME, nullptr)); + BKE_mesh_copy_parameters_for_eval(me, me_settings); + BKE_mesh_runtime_ensure_edit_data(me); + + me->runtime.wrapper_type = ME_WRAPPER_TYPE_BMESH; + if (cd_mask_extra) { + me->runtime.cd_mask_extra = *cd_mask_extra; + } + + /* Use edit-mesh directly where possible. */ + me->runtime.is_original = true; + + me->edit_mesh = static_cast<BMEditMesh *>(MEM_dupallocN(em)); + me->edit_mesh->is_shallow_copy = true; + + /* Make sure we crash if these are ever used. */ +#ifdef DEBUG + me->totvert = INT_MAX; + me->totedge = INT_MAX; + me->totpoly = INT_MAX; + me->totloop = INT_MAX; +#else + me->totvert = 0; + me->totedge = 0; + me->totpoly = 0; + me->totloop = 0; +#endif + + EditMeshData *edit_data = me->runtime.edit_data; + edit_data->vertexCos = vert_coords; + return me; +} + +Mesh *BKE_mesh_wrapper_from_editmesh(BMEditMesh *em, + const CustomData_MeshMasks *cd_mask_extra, + const Mesh *me_settings) +{ + return BKE_mesh_wrapper_from_editmesh_with_coords(em, cd_mask_extra, nullptr, me_settings); +} + +void BKE_mesh_wrapper_ensure_mdata(Mesh *me) +{ + ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex; + BLI_mutex_lock(mesh_eval_mutex); + + if (me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { + BLI_mutex_unlock(mesh_eval_mutex); + return; + } + + /* Must isolate multithreaded tasks while holding a mutex lock. */ + blender::threading::isolate_task([&]() { + const eMeshWrapperType geom_type_orig = static_cast<eMeshWrapperType>( + me->runtime.wrapper_type); + me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; + + switch (geom_type_orig) { + case ME_WRAPPER_TYPE_MDATA: + case ME_WRAPPER_TYPE_SUBD: { + break; /* Quiet warning. */ + } + case ME_WRAPPER_TYPE_BMESH: { + me->totvert = 0; + me->totedge = 0; + me->totpoly = 0; + me->totloop = 0; + + BLI_assert(me->edit_mesh != nullptr); + BLI_assert(me->runtime.edit_data != nullptr); + + BMEditMesh *em = me->edit_mesh; + BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra); + + /* Adding original index layers assumes that all BMesh mesh wrappers are created from + * original edit mode meshes (the only case where adding original indices makes sense). + * If that assumption is broken, the layers might be incorrect in that they might not + * actually be "original". + * + * There is also a performance aspect, where this also assumes that original indices are + * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not + * harmful. */ + BKE_mesh_ensure_default_orig_index_customdata(me); + + EditMeshData *edit_data = me->runtime.edit_data; + if (edit_data->vertexCos) { + BKE_mesh_vert_coords_apply(me, edit_data->vertexCos); + me->runtime.is_original = false; + } + break; + } + } + + if (me->runtime.wrapper_type_finalize) { + BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra); + } + }); + + BLI_mutex_unlock(mesh_eval_mutex); +} + +bool BKE_mesh_wrapper_minmax(const Mesh *me, float min[3], float max[3]) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_BMESH: + return BKE_editmesh_cache_calc_minmax(me->edit_mesh, me->runtime.edit_data, min, max); + case ME_WRAPPER_TYPE_MDATA: + case ME_WRAPPER_TYPE_SUBD: + return BKE_mesh_minmax(me, min, max); + } + BLI_assert_unreachable(); + return false; +} + +/* -------------------------------------------------------------------- */ +/** \name Mesh Coordinate Access + * \{ */ + +void BKE_mesh_wrapper_vert_coords_copy(const Mesh *me, + float (*vert_coords)[3], + int vert_coords_len) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_BMESH: { + BMesh *bm = me->edit_mesh->bm; + BLI_assert(vert_coords_len <= bm->totvert); + EditMeshData *edit_data = me->runtime.edit_data; + if (edit_data->vertexCos != nullptr) { + for (int i = 0; i < vert_coords_len; i++) { + copy_v3_v3(vert_coords[i], edit_data->vertexCos[i]); + } + } + else { + BMIter iter; + BMVert *v; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + copy_v3_v3(vert_coords[i], v->co); + } + } + return; + } + case ME_WRAPPER_TYPE_MDATA: + case ME_WRAPPER_TYPE_SUBD: { + BLI_assert(vert_coords_len <= me->totvert); + const MVert *mvert = me->mvert; + for (int i = 0; i < vert_coords_len; i++) { + copy_v3_v3(vert_coords[i], mvert[i].co); + } + return; + } + } + BLI_assert_unreachable(); +} + +void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const Mesh *me, + float (*vert_coords)[3], + int vert_coords_len, + const float mat[4][4]) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_BMESH: { + BMesh *bm = me->edit_mesh->bm; + BLI_assert(vert_coords_len == bm->totvert); + EditMeshData *edit_data = me->runtime.edit_data; + if (edit_data->vertexCos != nullptr) { + for (int i = 0; i < vert_coords_len; i++) { + mul_v3_m4v3(vert_coords[i], mat, edit_data->vertexCos[i]); + } + } + else { + BMIter iter; + BMVert *v; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + mul_v3_m4v3(vert_coords[i], mat, v->co); + } + } + return; + } + case ME_WRAPPER_TYPE_MDATA: + case ME_WRAPPER_TYPE_SUBD: { + BLI_assert(vert_coords_len == me->totvert); + const MVert *mvert = me->mvert; + for (int i = 0; i < vert_coords_len; i++) { + mul_v3_m4v3(vert_coords[i], mat, mvert[i].co); + } + return; + } + } + BLI_assert_unreachable(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Array Length Access + * \{ */ + +int BKE_mesh_wrapper_vert_len(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_BMESH: + return me->edit_mesh->bm->totvert; + case ME_WRAPPER_TYPE_MDATA: + case ME_WRAPPER_TYPE_SUBD: + return me->totvert; + } + BLI_assert_unreachable(); + return -1; +} + +int BKE_mesh_wrapper_edge_len(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_BMESH: + return me->edit_mesh->bm->totedge; + case ME_WRAPPER_TYPE_MDATA: + case ME_WRAPPER_TYPE_SUBD: + return me->totedge; + } + BLI_assert_unreachable(); + return -1; +} + +int BKE_mesh_wrapper_loop_len(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_BMESH: + return me->edit_mesh->bm->totloop; + case ME_WRAPPER_TYPE_MDATA: + case ME_WRAPPER_TYPE_SUBD: + return me->totloop; + } + BLI_assert_unreachable(); + return -1; +} + +int BKE_mesh_wrapper_poly_len(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_BMESH: + return me->edit_mesh->bm->totface; + case ME_WRAPPER_TYPE_MDATA: + case ME_WRAPPER_TYPE_SUBD: + return me->totpoly; + } + BLI_assert_unreachable(); + return -1; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CPU Subdivision Evaluation + * \{ */ + +static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) +{ + SubsurfModifierData *smd = BKE_object_get_last_subsurf_modifier(ob); + if (!smd) { + return me; + } + + /* Initialize the settings before ensuring the descriptor as this is checked to decide whether + * subdivision is needed at all, and checking the descriptor status might involve checking if the + * data is out-of-date, which is a very expensive operation. */ + SubdivToMeshSettings mesh_settings; + mesh_settings.resolution = me->runtime.subsurf_resolution; + mesh_settings.use_optimal_display = me->runtime.subsurf_use_optimal_display; + + if (mesh_settings.resolution < 3) { + return me; + } + + const bool apply_render = me->runtime.subsurf_apply_render; + + SubdivSettings subdiv_settings; + BKE_subsurf_modifier_subdiv_settings_init(&subdiv_settings, smd, apply_render); + if (subdiv_settings.level == 0) { + return me; + } + + SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd); + + Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(smd, &subdiv_settings, me, false); + if (subdiv == nullptr) { + /* Happens on bad topology, but also on empty input mesh. */ + return me; + } + + Mesh *subdiv_mesh = BKE_subdiv_to_mesh(subdiv, &mesh_settings, me); + + if (subdiv != runtime_data->subdiv) { + BKE_subdiv_free(subdiv); + } + + if (subdiv_mesh != me) { + if (me->runtime.mesh_eval != nullptr) { + BKE_id_free(nullptr, me->runtime.mesh_eval); + } + me->runtime.mesh_eval = subdiv_mesh; + me->runtime.wrapper_type = ME_WRAPPER_TYPE_SUBD; + } + + return me->runtime.mesh_eval; +} + +Mesh *BKE_mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) +{ + ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex; + BLI_mutex_lock(mesh_eval_mutex); + + if (me->runtime.wrapper_type == ME_WRAPPER_TYPE_SUBD) { + BLI_mutex_unlock(mesh_eval_mutex); + return me->runtime.mesh_eval; + } + + Mesh *result; + + /* Must isolate multithreaded tasks while holding a mutex lock. */ + blender::threading::isolate_task([&]() { result = mesh_wrapper_ensure_subdivision(ob, me); }); + + BLI_mutex_unlock(mesh_eval_mutex); + return result; +} + +/** \} */ |