Blender V4.3
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 "BLI_string_ref.hh"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "BLI_vector.hh"
#include "BKE_attribute.hh"
#include "BKE_customdata.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_runtime.hh"
#include "BKE_multires.hh"
#include "BKE_key.hh"
#include "BKE_main.hh"
#include "DEG_depsgraph_query.hh"
#include "bmesh.hh"
#include "intern/bmesh_private.hh"
#include "CLG_log.h"

Go to the source code of this file.

Classes

struct  MeshToBMeshLayerInfo
 
struct  BMeshToMeshLayerInfo
 

Namespaces

namespace  blender
 

Functions

bool BM_attribute_stored_in_bmesh_builtin (const StringRef name)
 
static BMFacebm_face_create_from_mpoly (BMesh &bm, Span< int > face_verts, Span< int > face_edges, Span< BMVert * > vtable, Span< BMEdge * > etable)
 
static Vector< MeshToBMeshLayerInfomesh_to_bm_copy_info_calc (const CustomData &mesh_data, CustomData &bm_data)
 
static void mesh_attributes_copy_to_bmesh_block (CustomData &data, const Span< MeshToBMeshLayerInfo > copy_info, const int mesh_index, BMHeader &header)
 
void BM_mesh_bm_from_me (BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
 
static BMVert ** bm_to_mesh_vertex_map (BMesh *bm, const int old_verts_num)
 BMesh -> Mesh.
 
static void assert_bmesh_has_no_mesh_only_attributes (const BMesh &bm)
 
static void bmesh_to_mesh_calc_object_remap (Main &bmain, Mesh &mesh, BMesh &bm, const int old_totvert)
 
static Vector< BMeshToMeshLayerInfobm_to_mesh_copy_info_calc (const CustomData &bm_data, CustomData &mesh_data)
 
static void blender::bm_vert_table_build (BMesh &bm, MutableSpan< const BMVert * > table, bool &need_select_vert, bool &need_hide_vert)
 
static void blender::bm_edge_table_build (BMesh &bm, MutableSpan< const BMEdge * > table, bool &need_select_edge, bool &need_hide_edge, bool &need_sharp_edge, bool &need_uv_seams)
 
static void blender::bm_face_loop_table_build (BMesh &bm, MutableSpan< const BMFace * > face_table, MutableSpan< const BMLoop * > loop_table, bool &need_select_poly, bool &need_hide_poly, bool &need_sharp_face, bool &need_material_index, Vector< int > &loop_layers_not_to_copy)
 
static void blender::bmesh_block_copy_to_mesh_attributes (const Span< BMeshToMeshLayerInfo > copy_info, const int mesh_index, const void *block)
 
static void blender::bm_to_mesh_verts (const BMesh &bm, const Span< const BMVert * > bm_verts, Mesh &mesh, MutableSpan< bool > select_vert, MutableSpan< bool > hide_vert)
 
static void blender::bm_to_mesh_edges (const BMesh &bm, const Span< const BMEdge * > bm_edges, Mesh &mesh, MutableSpan< bool > select_edge, MutableSpan< bool > hide_edge, MutableSpan< bool > sharp_edge, MutableSpan< bool > uv_seams)
 
static void blender::bm_to_mesh_faces (const BMesh &bm, const Span< const BMFace * > bm_faces, Mesh &mesh, MutableSpan< bool > select_poly, MutableSpan< bool > hide_poly, MutableSpan< bool > sharp_faces, MutableSpan< int > material_indices)
 
static void blender::bm_to_mesh_loops (const BMesh &bm, const Span< const BMLoop * > bm_loops, Mesh &mesh)
 
void BM_mesh_bm_to_me (Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
 
void BM_mesh_bm_to_me_compact (BMesh &bm, Mesh &mesh, const CustomData_MeshMasks *mask, const bool add_mesh_attributes)
 
void BM_mesh_bm_to_me_for_eval (BMesh &bm, Mesh &mesh, 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 #35170 & #44415.

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 mesh->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 won't apply its deltas to other keys and if 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, MutableSpan< float3 > positions, 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

  • Editmode operations when a shape key-block is active edits only that key-block.
  • The first Basis key-block always matches the Mesh verts.
  • Changing vertex locations of any Basis will apply offsets to those shape keys using this as their Basis.

Entering EditMode - #BM_mesh_bm_from_me

  • The active key-block is used for BMesh vertex locations on entering edit-mode. So obviously the meshes vertex locations remain unchanged and the shape key itself is not being edited directly. Simply the BMVert.co is a initialized from active shape key (when its set).
  • All key-blocks are added as CustomData layers (read code for details).

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.

  • Vertex locations (possibly modified from initial active key-block) are copied directly into the mesh position attribute. (special confusing note that these may be restored later, when editing the 'Basis', read on).
  • if the 'Key' is relative, and the active key-block is the basis for ANY other key-blocks - get an array of offsets between the new vertex locations and the original shape key (before entering edit-mode), these offsets get applied later on to inactive key-blocks using the active one (which we are editing) as their Basis.

Copying the locations back to the shape keys is quite confusing... One main area of confusion is that when editing a 'Basis' key-block 'mesh->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:

  • The CD_SHAPEKEY layer is only used in edit-mode, and the Mesh.key is only used in object-mode. Although the CD_SHAPEKEY custom-data layer is converted into Key data-blocks for each undo-step while in edit-mode.
  • The CD_SHAPE_KEYINDEX layer is used to check if vertices existed when entering edit-mode. Values of the indices are only used for shape-keys when the CD_SHAPEKEY layer can't be found, allowing coordinates from the Key to be used to prevent data-loss. These indices are also used to maintain correct indices for hook modifiers and vertex parents.

Definition in file bmesh_mesh_convert.cc.

Function Documentation

◆ assert_bmesh_has_no_mesh_only_attributes()

static void assert_bmesh_has_no_mesh_only_attributes ( const BMesh & bm)
static

◆ BM_attribute_stored_in_bmesh_builtin()

bool BM_attribute_stored_in_bmesh_builtin ( const blender::StringRef name)
Returns
Whether attributes with the given name are stored in special flags or fields in BMesh rather than in the regular custom data blocks.

Definition at line 120 of file bmesh_mesh_convert.cc.

References ELEM.

Referenced by CustomData_shallow_copy_remove_non_bmesh_attributes(), and blender::ed::sculpt_paint::dyntopo::dyntopo_supports_layer().

◆ bm_face_create_from_mpoly()

static BMFace * bm_face_create_from_mpoly ( BMesh & bm,
Span< int > face_verts,
Span< int > face_edges,
Span< BMVert * > vtable,
Span< BMEdge * > etable )
static

◆ BM_mesh_bm_from_me()

void BM_mesh_bm_from_me ( BMesh * bm,
const Mesh * mesh,
const BMeshFromMeshParams * params )

Definition at line 214 of file bmesh_mesh_convert.cc.

References BMesh::act_face, blender::Vector< T, InlineBufferCapacity, Allocator >::append(), BKE_uv_map_edge_select_name_get(), BKE_uv_map_pin_name_get(), BKE_uv_map_vert_select_name_get(), BLI_array_alloca, BLI_assert, BLI_findlink(), BLI_listbase_count(), BLI_SCOPED_DEFER, bm, BM_CREATE_SKIP_CD, BM_EDGE, BM_edge_create(), BM_edge_select_set(), BM_ELEM_CD_GET_VOID_P, BM_ELEM_CD_SET_INT, BM_elem_flag_enable, BM_ELEM_HIDDEN, BM_elem_index_set, BM_ELEM_SEAM, BM_ELEM_SMOOTH, BM_FACE, bm_face_create_from_mpoly(), BM_FACE_FIRST_LOOP, BM_face_normal_update(), BM_face_select_set(), BM_LOOP, BM_select_history_clear(), BM_select_history_store_notest, BM_VERT, BM_vert_create(), BM_vert_select_set(), CD_CONSTRUCT, CD_MASK_BMESH, CD_PROP_BOOL, CD_PROP_FLOAT2, CD_SET_DEFAULT, CD_SHAPE_KEYINDEX, CD_SHAPEKEY, copy_v3_v3(), CustomData_add_layer(), CustomData_add_layer_named(), CustomData_bmesh_init_pool(), CustomData_bmesh_merge_layout(), CustomData_free_layer_named(), CustomData_get_layer_index_n(), CustomData_get_layer_name(), CustomData_get_named_layer_index(), CustomData_get_offset(), CustomData_has_layer(), CustomData_init_layout_from(), CustomData_MeshMasks_update(), CustomData_number_of_layers(), CustomData_shallow_copy_remove_non_bmesh_attributes(), KeyBlock::data, DEG_is_original_id(), e, BMesh::edata, BMesh::elem_index_dirty, float, BMFace::head, BMLoop::head, BMVert::head, BMHeader::hflag, MSelect::index, blender::Array< T, InlineBufferCapacity, Allocator >::is_empty(), blender::Span< T >::is_empty(), CustomData::layers, BMesh::ldata, LISTBASE_FOREACH, BMFace::mat_nr, MAX_CUSTOMDATA_LAYER_NAME, ME_ESEL, ME_FSEL, ME_VSEL, MEM_SAFE_FREE, mesh_attributes_copy_to_bmesh_block(), mesh_to_bm_copy_info_calc(), min_ii(), KeyBlock::name, BMLoop::next, KeyBlock::next, BMVert::no, params, BMesh::pdata, printf, blender::Array< T, InlineBufferCapacity, Allocator >::reinitialize(), BMesh::shapenr, blender::Span< T >::slice(), KeyBlock::totelem, BMesh::totface, CustomData::totlayer, BMesh::totvert, MSelect::type, CustomDataLayer::uid, KeyBlock::uid, UNLIKELY, v, and BMesh::vdata.

Referenced by blender::ed::sculpt_paint::trim::apply_trim(), 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(), blender::ed::sculpt_paint::face_set::delete_geometry(), ED_uvedit_add_simple_uvs(), edbm_separate_exec(), blender::ed::sculpt_paint::dyntopo::enable_ex(), geometry_extract_apply(), get_bmesh_from_mesh(), blender::geometry::boolean::mesh_bm_concat(), paint_mask_slice_exec(), undomesh_to_editmesh(), and blender::ed::sculpt_paint::trim::update_normals().

◆ BM_mesh_bm_to_me()

◆ BM_mesh_bm_to_me_compact()

void BM_mesh_bm_to_me_compact ( BMesh & bm,
Mesh & mesh,
const CustomData_MeshMasks * mask,
bool add_mesh_attributes )

◆ BM_mesh_bm_to_me_for_eval()

void BM_mesh_bm_to_me_for_eval ( BMesh & bm,
Mesh & mesh,
const CustomData_MeshMasks * cd_mask_extra )

A version of BM_mesh_bm_to_me intended for getting the mesh to pass to the modifier stack for evaluation, instead of mode switching (where we make sure all data is kept and do expensive lookups to maintain shape keys).

Key differences:

  • Don't support merging with existing mesh.
  • Ignore shape-keys.
  • Ignore vertex-parents.
  • Ignore selection history.
  • Uses CD_MASK_DERIVEDMESH instead of CD_MASK_MESH.
Note
Was cddm_from_bmesh_ex in 2.7x, removed MFace support.

Definition at line 1799 of file bmesh_mesh_convert.cc.

References bm, BM_mesh_bm_to_me_compact(), CD_MASK_DERIVEDMESH, and CustomData_MeshMasks_update().

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

◆ bm_to_mesh_copy_info_calc()

◆ bm_to_mesh_shape()

static void bm_to_mesh_shape ( BMesh * bm,
Key * key,
MutableSpan< float3 > positions,
const bool active_shapekey_to_mvert )
static

Update key with shape key data stored in bm.

Parameters
bmThe source BMesh.
keyThe destination key.
positionsThe destination vertex array (in some situations its coordinates are updated).
active_shapekey_to_mvertWhen editing a non-basis shape key, the coordinates for the basis are typically copied into the positions 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 the mesh position. See BMeshToMeshParams.active_shapekey_to_mvert doc-string.

Definition at line 756 of file bmesh_mesh_convert.cc.

References add_v3_v3(), BKE_keyblock_add(), BKE_keyblock_get_dependent_keys(), 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_INDEX, LOG, MEM_freeN(), MEM_mallocN, MEM_reallocN, MEM_SAFE_FREE, 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.

◆ 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 728 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,
const int old_verts_num )
static

◆ bmesh_to_mesh_calc_object_remap()

static void bmesh_to_mesh_calc_object_remap ( Main & bmain,
Mesh & mesh,
BMesh & bm,
const int old_totvert )
static

◆ mesh_attributes_copy_to_bmesh_block()

static void mesh_attributes_copy_to_bmesh_block ( CustomData & data,
const Span< MeshToBMeshLayerInfo > copy_info,
const int mesh_index,
BMHeader & header )
static

◆ mesh_to_bm_copy_info_calc()

Variable Documentation

◆ LOG

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

Definition at line 109 of file bmesh_mesh_convert.cc.

Referenced by bm_to_mesh_shape().