Blender  V3.3
Functions | Variables
bmesh_mesh_convert.cc File Reference
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
#include "BLI_array.hh"
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_span.hh"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_multires.h"
#include "BKE_key.h"
#include "BKE_main.h"
#include "DEG_depsgraph_query.h"
#include "bmesh.h"
#include "intern/bmesh_private.h"
#include "CLG_log.h"

Go to the source code of this file.

Functions

void BM_mesh_cd_flag_ensure (BMesh *bm, Mesh *mesh, const char cd_flag)
 
void BM_mesh_cd_flag_apply (BMesh *bm, const char cd_flag)
 
char BM_mesh_cd_flag_from_bmesh (BMesh *bm)
 
static BMFacebm_face_create_from_mpoly (BMesh &bm, Span< MLoop > loops, Span< BMVert * > vtable, Span< BMEdge * > etable)
 
void BM_mesh_bm_from_me (BMesh *bm, const Mesh *me, const struct BMeshFromMeshParams *params)
 
static BMVert ** bm_to_mesh_vertex_map (BMesh *bm, int ototvert)
 BMesh -> Mesh. More...
 
BLI_INLINE void bmesh_quick_edgedraw_flag (MEdge *med, BMEdge *e)
 
void BM_mesh_bm_to_me (Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params)
 
void BM_mesh_bm_to_me_for_eval (BMesh *bm, Mesh *me, const CustomData_MeshMasks *cd_mask_extra)
 
Edit-Mesh to Shape Key Conversion

There are some details relating to using data from shape keys that need to be considered carefully for shape key synchronization logic.

Key Block Usage


Key blocks (data in Mesh.key must be used carefully).

They can be used to query which key blocks are relative to the basis since it's not possible to add/remove/reorder key blocks while in edit-mode.

Key Block Coordinates

Key blocks locations must not be used. This was done from v2.67 to 3.0, causing bugs T35170 & T44415.

Shape key synchronizing could work under the assumption that the key-block is fixed-in-place when entering edit-mode allowing them to be used as a reference when exiting. It often does work but isn't reliable since for e.g. rendering may flush changes from the edit-mesh to the key-block (there are a handful of other situations where changes may be flushed, see ED_editors_flush_edits and related functions). When using undo, it's not known if the data in key-block is from the past or future, so just don't use this data as it causes pain and suffering for users and developers alike.

Instead, use the shape-key values stored in CD_SHAPEKEY since they are reliably based on the original locations, unless explicitly manipulated. It's important to write the final shape-key values back to the CD_SHAPEKEY so applying the difference between the original-basis and the new coordinates isn't done multiple times. Therefore ED_editors_flush_edits and other flushing calls will update both the Mesh.key and the edit-mode CD_SHAPEKEY custom-data layers.

WARNING: There is an exception to the rule of ignoring coordinates in the destination: that is when shape-key data in bm can't be found (which is itself an error/exception). In this case our own rule is violated as the alternative is losing the shape-data entirely.

Flushing Coordinates Back to the BMesh

The edit-mesh may be flushed back to the Mesh and Key used to generate it. When this is done, the new values are written back to the BMesh's CD_SHAPEKEY as well. This is necessary when editing basis-shapes so the difference in shape keys is not applied multiple times. If it were important to avoid it could be skipped while exiting edit-mode (as the entire BMesh is freed in that case), however it's just copying back a float[3] so the work to check if it's necessary isn't worth the overhead.

In general updating the BMesh's CD_SHAPEKEY makes shake-key logic easier to reason about since it means flushing data back to the mesh has the same behavior as exiting and entering edit-mode (a more common operation). Meaning there is one less corner-case to have to consider.

Exceptional Cases


There are some situations that should not happen in typical usage but are still handled in this code, since failure to handle them could loose user-data. These could be investigated further since if they never happen in practice, we might consider removing them. However, the possibility of an mesh directly being modified by Python or some other low level logic that changes key-blocks means there is a potential this to happen so keeping code to these cases remain supported.

  • Custom Data & Mesh Key Block Synchronization. Key blocks in me->key->block should always have an associated CD_SHAPEKEY layer in bm->vdata. If they don't there are two fall-backs for setting the location,
    • Use the value from the original shape key WARNING: this is technically incorrect! (see note on "Key Block Usage").
    • Use the current vertex location, Also not correct but it's better then having it zeroed for e.g.
  • Missing key-index layer. In this case the basis key wont apply it's deltas to other keys and in the case a shape-key layer is missing, its coordinates will be initialized from the edit-mesh vertex locations instead of attempting to remap the shape-keys coordinates.
Note
These cases are considered abnormal and shouldn't occur in typical usage. A warning is logged in this case to help troubleshooting bugs with shape-keys.
static int bm_to_mesh_shape_layer_index_from_kb (BMesh *bm, KeyBlock *currkey)
 
static void bm_to_mesh_shape (BMesh *bm, Key *key, MVert *mvert, const bool active_shapekey_to_mvert)
 

Variables

static CLG_LogRef LOG = {"bmesh.mesh.convert"}
 

Detailed Description

BM mesh conversion functions.

Converting Shape Keys

When converting to/from a Mesh/BMesh you can optionally pass a shape key to edit. This has the effect of editing the shape key-block rather than the original mesh vertex coords (although additional geometry is still allowed and uses fallback locations on converting).

While this works for any mesh/bmesh this is made use of by entering and exiting edit-mode.

There are comments in code but this should help explain the general intention as to how this works converting from/to bmesh.

User Perspective

Entering EditMode - #BM_mesh_bm_from_me

Exiting EditMode - #BM_mesh_bm_to_me

This is where the most confusing code is! Won't attempt to document the details here, for that read the code. But basics are as follows.

Copying the locations back to the shape keys is quite confusing... One main area of confusion is that when editing a 'Basis' key-block 'me->key->refkey' The coords are written into the mesh, from the users perspective the Basis coords are written into the mesh when exiting edit-mode.

When not editing the 'Basis', the original vertex locations (stored in the mesh and unchanged during edit-mode), are copied back into the mesh.

This has the effect from the users POV of leaving the mesh un-touched, and only editing the active shape key-block.

Other Notes

Other details noted here which might not be so obvious:

Definition in file bmesh_mesh_convert.cc.

Function Documentation

◆ bm_face_create_from_mpoly()

static BMFace* bm_face_create_from_mpoly ( BMesh bm,
Span< MLoop loops,
Span< BMVert * >  vtable,
Span< BMEdge * >  etable 
)
static

◆ BM_mesh_bm_from_me()

void BM_mesh_bm_from_me ( BMesh bm,
const Mesh me,
const struct BMeshFromMeshParams params 
)

Definition at line 204 of file bmesh_mesh_convert.cc.

References BMesh::act_face, Mesh::act_face, BKE_mesh_vertex_normals_ensure(), BLI_array_alloca, BLI_assert, BLI_findlink(), BLI_listbase_count(), Key::block, bm, BM_CREATE_SKIP_CD, BM_EDGE, BM_edge_create(), BM_edge_flag_from_mflag(), BM_edge_select_set(), BM_ELEM_CD_GET_VOID_P, BM_ELEM_CD_SET_FLOAT, BM_ELEM_CD_SET_INT, BM_elem_index_set, BM_FACE, bm_face_create_from_mpoly(), BM_FACE_FIRST_LOOP, BM_face_flag_from_mflag(), BM_face_normal_update(), BM_face_select_set(), BM_LOOP, BM_mesh_cd_flag_apply(), BM_mesh_cd_flag_from_bmesh(), BM_select_history_clear(), BM_select_history_store_notest, BM_VERT, BM_vert_create(), BM_vert_flag_from_mflag(), BM_vert_select_set(), CD_ASSIGN, CD_BWEIGHT, CD_CALLOC, CD_CREASE, CD_DEFAULT, Mesh::cd_flag, CD_MASK_BMESH, CD_SHAPE_KEYINDEX, CD_SHAPEKEY, copy_v3_v3(), CustomData_add_layer(), CustomData_add_layer_named(), CustomData_bmesh_init_pool(), CustomData_bmesh_merge(), CustomData_copy(), CustomData_get_layer_index_n(), CustomData_get_offset(), CustomData_has_layer(), CustomData_MeshMasks_update(), CustomData_number_of_layers(), CustomData_to_bmesh_block(), BMHeader::data, KeyBlock::data, DEG_is_original_id(), e, BMesh::edata, Mesh::edata, BMesh::elem_index_dirty, ListBase::first, float(), BMVert::head, BMLoop::head, BMFace::head, BMHeader::hflag, Mesh::id, MSelect::index, blender::Array< T, InlineBufferCapacity, Allocator >::is_empty(), Mesh::key, CustomData::layers, BMesh::ldata, Mesh::ldata, LISTBASE_FOREACH, mask(), BMFace::mat_nr, ME_CDFLAG_EDGE_BWEIGHT, ME_CDFLAG_EDGE_CREASE, ME_CDFLAG_VERT_BWEIGHT, ME_ESEL, ME_FACE_SEL, ME_FSEL, ME_VSEL, Mesh::medge, min_ii(), Mesh::mloop, Mesh::mpoly, Mesh::mselect, Mesh::mvert, KeyBlock::name, ID::name, BMLoop::next, KeyBlock::next, BMVert::no, params, BMesh::pdata, Mesh::pdata, blender::Array< T, InlineBufferCapacity, Allocator >::reinitialize(), SELECT, BMesh::shapenr, Mesh::totedge, KeyBlock::totelem, BMesh::totface, CustomData::totlayer, Mesh::totloop, Mesh::totpoly, Mesh::totselect, BMesh::totvert, Mesh::totvert, MSelect::type, CustomDataLayer::uid, KeyBlock::uid, Key::uidgen, UNLIKELY, v, v1, v2, BMesh::vdata, and Mesh::vdata.

Referenced by bc_triangulate_mesh(), BKE_mesh_remesh_voxel_fix_poles(), BKE_mesh_to_bmesh_ex(), BMD_mesh_bm_create(), bmo_mesh_to_bmesh_exec(), bpy_bmesh_from_mesh(), bpy_bmesh_from_object(), ED_uvedit_add_simple_uvs(), edbm_separate_exec(), geometry_extract_apply(), get_bmesh_from_mesh(), blender::nodes::node_geo_edge_split_cc::mesh_edge_split(), paint_mask_slice_exec(), SCULPT_dynamic_topology_enable_ex(), sculpt_face_set_create_exec(), sculpt_face_set_delete_geometry(), sculpt_face_sets_init_flood_fill(), sculpt_face_sets_init_loop(), sculpt_gesture_apply_trim(), sculpt_gesture_trim_normals_update(), and undomesh_to_editmesh().

◆ BM_mesh_bm_to_me()

void BM_mesh_bm_to_me ( Main bmain,
BMesh bm,
Mesh me,
const struct BMeshToMeshParams params 
)

Definition at line 905 of file bmesh_mesh_convert.cc.

References BMesh::act_face, Mesh::act_face, BKE_mesh_clear_derived_normals(), BKE_mesh_runtime_clear_geometry(), BKE_mesh_update_customdata_pointers(), BLI_assert, BLI_listbase_count(), bm, BM_CHECK_ELEMENT, BM_EDGE, BM_edge_flag_to_mflag(), BM_EDGES_OF_MESH, BM_ELEM_CD_GET_FLOAT_AS_UCHAR, BM_ELEM_CD_SET_INT, BM_elem_index_get, BM_elem_index_set, BM_FACE, BM_FACE_FIRST_LOOP, BM_face_flag_to_mflag(), BM_FACES_OF_MESH, BM_ITER_MESH, BM_ITER_MESH_INDEX, BM_mesh_cd_flag_from_bmesh(), bm_to_mesh_shape(), bm_to_mesh_vertex_map(), BM_VERT, BM_vert_flag_to_mflag(), BM_VERTS_OF_MESH, bmesh_quick_edgedraw_flag(), MVert::bweight, MEdge::bweight, CD_ASSIGN, CD_BWEIGHT, CD_CALLOC, CD_CREASE, Mesh::cd_flag, CD_MASK_MESH, CD_MEDGE, CD_MLOOP, CD_MPOLY, CD_MVERT, CD_SHAPE_KEYINDEX, BMVert::co, MVert::co, copy_v3_v3(), MEdge::crease, CustomData_add_layer(), CustomData_copy(), CustomData_free(), CustomData_from_bmesh_block(), CustomData_get_offset(), CustomData_MeshMasks_update(), BMHeader::data, BMLoop::e, e, MLoop::e, BMesh::edata, Mesh::edata, ELEM, BMesh::elem_index_dirty, eModifierType_Hook, Mesh::fdata, MVert::flag, MEdge::flag, MPoly::flag, BMVert::head, BMLoop::head, BMFace::head, MSelect::index, HookModifierData::indexar, HookModifierData::indexar_num, Mesh::key, BMesh::ldata, Mesh::ldata, BMFace::len, LISTBASE_FOREACH, LISTBASE_FOREACH_INDEX, MPoly::loopstart, mask(), BMFace::mat_nr, MPoly::mat_nr, ME_ESEL, ME_FSEL, ME_VSEL, MEM_callocN, MEM_freeN, MEM_mallocN, MEM_SAFE_FREE, Mesh::mselect, multires_topology_changed(), Mesh::mvert, BMLoop::next, Main::objects, params, PARVERT1, PARVERT3, BMesh::pdata, Mesh::pdata, BMesh::selected, BMesh::totedge, Mesh::totedge, BMesh::totface, Mesh::totface, BMesh::totloop, Mesh::totloop, MPoly::totloop, Mesh::totpoly, Mesh::totselect, BMesh::totvert, Mesh::totvert, MSelect::type, BMLoop::v, v, MLoop::v, MEdge::v1, MEdge::v2, BMesh::vdata, and Mesh::vdata.

Referenced by bc_triangulate_mesh(), BKE_mesh_from_bmesh_nomain(), BKE_mesh_mirror_apply_mirror_on_axis(), bmo_bmesh_to_mesh_exec(), bpy_bmesh_to_mesh(), blender::nodes::node_geo_mesh_primitive_ico_sphere_cc::create_ico_sphere_mesh(), ED_uvedit_add_simple_uvs(), EDBM_mesh_load_ex(), edbm_separate_exec(), mesh_separate_arrays(), mesh_separate_loose(), mesh_separate_tagged(), modifyMesh(), multires_unsubdivide_to_basemesh(), prepare_mesh_for_viewport_render(), sculpt_face_set_delete_geometry(), sculptsession_bm_to_me_update_data_only(), and undomesh_from_editmesh().

◆ BM_mesh_bm_to_me_for_eval()

void BM_mesh_bm_to_me_for_eval ( BMesh bm,
Mesh me,
const CustomData_MeshMasks cd_mask_extra 
)

Definition at line 1168 of file bmesh_mesh_convert.cc.

References BKE_mesh_clear_derived_normals(), BKE_mesh_update_customdata_pointers(), BLI_assert, bm, BM_EDGE, BM_edge_flag_to_mflag(), BM_EDGES_OF_MESH, BM_ELEM_CD_GET_FLOAT_AS_UCHAR, BM_elem_index_get, BM_elem_index_set, BM_FACE, BM_FACE_FIRST_LOOP, BM_face_flag_to_mflag(), BM_FACES_OF_MESH, BM_ITER_MESH_INDEX, BM_LOOP, BM_mesh_cd_flag_from_bmesh(), BM_VERT, BM_vert_flag_to_mflag(), BM_VERTS_OF_MESH, MEdge::bweight, CD_BWEIGHT, CD_CALLOC, CD_CREASE, Mesh::cd_flag, CD_MASK_DERIVEDMESH, CD_MASK_SHAPEKEY, CD_MEDGE, CD_MLOOP, CD_MPOLY, CD_MVERT, BMVert::co, copy_v3_v3(), MEdge::crease, CustomData_add_layer(), CustomData_from_bmesh_block(), CustomData_get_offset(), CustomData_merge(), CustomData_MeshMasks_update(), BMHeader::data, Mesh_Runtime::deformed_only, BMLoop::e, MLoop::e, BMesh::edata, Mesh::edata, BMesh::elem_index_dirty, MEdge::flag, MPoly::flag, BMVert::head, BMEdge::head, BMLoop::head, BMFace::head, BMEdge::l, BMesh::ldata, Mesh::ldata, BMFace::len, MPoly::loopstart, mask(), BMFace::mat_nr, MPoly::mat_nr, ME_EDGEDRAW, Mesh::medge, Mesh::mloop, Mesh::mpoly, Mesh::mvert, BMLoop::next, BMesh::pdata, Mesh::pdata, BMLoop::radial_next, Mesh::runtime, BMesh::totedge, Mesh::totedge, BMesh::totface, Mesh::totface, BMesh::totloop, Mesh::totloop, MPoly::totloop, Mesh::totpoly, BMesh::totvert, Mesh::totvert, BMLoop::v, MLoop::v, BMEdge::v1, MEdge::v1, BMEdge::v2, MEdge::v2, BMesh::vdata, Mesh::vdata, and CustomData_MeshMasks::vmask.

Referenced by BKE_mesh_from_bmesh_for_eval_nomain(), and blender::ed::spreadsheet::spreadsheet_get_display_geometry_set().

◆ BM_mesh_cd_flag_apply()

void BM_mesh_cd_flag_apply ( BMesh bm,
const char  cd_flag 
)

◆ BM_mesh_cd_flag_ensure()

void BM_mesh_cd_flag_ensure ( BMesh bm,
Mesh mesh,
const char  cd_flag 
)

◆ BM_mesh_cd_flag_from_bmesh()

char BM_mesh_cd_flag_from_bmesh ( BMesh bm)

◆ bm_to_mesh_shape()

static void bm_to_mesh_shape ( BMesh bm,
Key key,
MVert mvert,
const bool  active_shapekey_to_mvert 
)
static

Update key with shape key data stored in bm.

Parameters
bmThe source BMesh.
keyThe destination key.
mvertThe destination vertex array (in some situations it's coordinates are updated).
active_shapekey_to_mvertWhen editing a non-basis shape key, the coordinates for the basis are typically copied into the mvert array since it makes sense for the meshes vertex coordinates to match the "Basis" key. When enabled, skip this step and copy BMVert.co directly to MVert.co, See BMeshToMeshParams.active_shapekey_to_mvert doc-string.

Definition at line 683 of file bmesh_mesh_convert.cc.

References add_v3_v3(), BKE_keyblock_add(), BKE_keyblock_is_basis(), BLI_assert, BLI_findlink(), Key::block, bm, BM_ELEM_CD_GET_INT, BM_ELEM_CD_GET_VOID_P, BM_ITER_MESH_INDEX, bm_to_mesh_shape_layer_index_from_kb(), BM_VERTS_OF_MESH, CD_SHAPE_KEYINDEX, CD_SHAPEKEY, CLOG_WARN, BMVert::co, copy_v3_v3(), CustomData_get_n_offset(), CustomData_get_offset(), Key::elemsize, ListBase::first, float(), KEY_RELATIVE, CustomData::layers, LISTBASE_FOREACH, LOG, MEM_freeN, MEM_mallocN, MEM_reallocN, CustomDataLayer::name, KeyBlock::next, ORIGINDEX_NONE, Key::refkey, BMesh::shapenr, sub_v3_v3v3(), CustomData::totlayer, BMesh::totvert, CustomDataLayer::type, Key::type, CustomDataLayer::uid, KeyBlock::uid, and BMesh::vdata.

Referenced by BM_mesh_bm_to_me().

◆ bm_to_mesh_shape_layer_index_from_kb()

static int bm_to_mesh_shape_layer_index_from_kb ( BMesh bm,
KeyBlock currkey 
)
static

Returns custom-data shape-key index from a key-block or -1

Note
could split this out into a more generic function.

Definition at line 655 of file bmesh_mesh_convert.cc.

References bm, CD_SHAPEKEY, CustomData::layers, CustomData::totlayer, CustomDataLayer::type, CustomDataLayer::uid, KeyBlock::uid, and BMesh::vdata.

Referenced by bm_to_mesh_shape().

◆ bm_to_mesh_vertex_map()

static BMVert** bm_to_mesh_vertex_map ( BMesh bm,
int  ototvert 
)
static

◆ bmesh_quick_edgedraw_flag()

BLI_INLINE void bmesh_quick_edgedraw_flag ( MEdge med,
BMEdge e 
)

Definition at line 888 of file bmesh_mesh_convert.cc.

References dot_v3v3(), e, MEdge::flag, ME_EDGEDRAW, and BMVert::no.

Referenced by BM_mesh_bm_to_me().

Variable Documentation

◆ LOG

CLG_LogRef LOG = {"bmesh.mesh.convert"}
static

Definition at line 102 of file bmesh_mesh_convert.cc.

Referenced by bm_to_mesh_shape().