/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bke */ #include #include "MEM_guardedalloc.h" /* Allow using deprecated functionality for .blend file I/O. */ #define DNA_DEPRECATED_ALLOW #include "DNA_key_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "BLI_array_utils.hh" #include "BLI_bounds.hh" #include "BLI_hash.h" #include "BLI_implicit_sharing.hh" #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math_matrix.hh" #include "BLI_math_vector.h" #include "BLI_math_vector.hh" #include "BLI_memory_counter.hh" #include "BLI_resource_scope.hh" #include "BLI_set.hh" #include "BLI_span.hh" #include "BLI_string.h" #include "BLI_task.hh" #include "BLI_time.h" #include "BLI_utildefines.h" #include "BLI_vector.hh" #include "BLI_virtual_array.hh" #include "BLT_translation.hh" #include "BKE_anim_data.hh" #include "BKE_anonymous_attribute_id.hh" #include "BKE_attribute.hh" #include "BKE_attribute_legacy_convert.hh" #include "BKE_attribute_storage.hh" #include "BKE_attribute_storage_blend_write.hh" #include "BKE_bake_data_block_id.hh" #include "BKE_bpath.hh" #include "BKE_deform.hh" #include "BKE_editmesh.hh" #include "BKE_editmesh_cache.hh" #include "BKE_global.hh" #include "BKE_idtype.hh" #include "BKE_key.hh" #include "BKE_lib_id.hh" #include "BKE_lib_query.hh" #include "BKE_main.hh" #include "BKE_material.hh" #include "BKE_mesh.hh" #include "BKE_mesh_legacy_convert.hh" #include "BKE_mesh_runtime.hh" #include "BKE_mesh_wrapper.hh" #include "BKE_modifier.hh" #include "BKE_multires.hh" #include "BKE_object.hh" #include "BKE_paint_bvh.hh" #include "DEG_depsgraph.hh" #include "DEG_depsgraph_query.hh" #include "BLO_read_write.hh" #include "CLG_log.h" #include "attribute_storage_access.hh" namespace blender { static CLG_LogRef LOG = {"geom.mesh"}; /** Using STACK_FIXED_DEPTH to keep the implementation in line with `pbvh.cc`. */ #define STACK_FIXED_DEPTH 100 static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata); static void mesh_init_data(ID *id) { Mesh *mesh = reinterpret_cast(id); INIT_DEFAULT_STRUCT_AFTER(mesh, id); CustomData_reset(&mesh->vert_data); CustomData_reset(&mesh->edge_data); CustomData_reset(&mesh->fdata_legacy); CustomData_reset(&mesh->face_data); CustomData_reset(&mesh->corner_data); new (&mesh->attribute_storage.wrap()) bke::AttributeStorage(); mesh->runtime = new bke::MeshRuntime(); mesh->face_sets_color_seed = BLI_hash_int(BLI_time_now_seconds_i() & UINT_MAX); } static void mesh_copy_data(Main *bmain, std::optional owner_library, ID *id_dst, const ID *id_src, const int flag) { Mesh *mesh_dst = reinterpret_cast(id_dst); const Mesh *mesh_src = reinterpret_cast(id_src); mesh_dst->runtime = new bke::MeshRuntime(); mesh_dst->runtime->deformed_only = mesh_src->runtime->deformed_only; /* Subd runtime.mesh_eval is not copied, will need to be reevaluated. */ mesh_dst->runtime->wrapper_type = (mesh_src->runtime->wrapper_type == ME_WRAPPER_TYPE_SUBD) ? ME_WRAPPER_TYPE_MDATA : mesh_src->runtime->wrapper_type; mesh_dst->runtime->subsurf_runtime_data = mesh_src->runtime->subsurf_runtime_data; mesh_dst->runtime->cd_mask_extra = mesh_src->runtime->cd_mask_extra; /* Copy face dot tags and edge tags, since meshes may be duplicated after a subsurf modifier or * node, but we still need to be able to draw face center vertices and "optimal edges" * differently. The tags may be cleared explicitly when the topology is changed. */ mesh_dst->runtime->subsurf_face_dot_tags = mesh_src->runtime->subsurf_face_dot_tags; mesh_dst->runtime->subsurf_optimal_display_edges = mesh_src->runtime->subsurf_optimal_display_edges; if ((mesh_src->id.tag & ID_TAG_NO_MAIN) == 0) { /* This is a direct copy of a main mesh, so for now it has the same topology. */ mesh_dst->runtime->deformed_only = true; } /* This option is set for run-time meshes that have been copied from the current object's mode. * Currently this is used for edit-mesh although it could be used for sculpt or other * kinds of data specific to an object's mode. * * The flag signals that the mesh hasn't been modified from the data that generated it, * allowing us to use the object-mode data for drawing. * * While this could be the caller's responsibility, keep here since it's * highly unlikely we want to create a duplicate and not use it for drawing. */ mesh_dst->runtime->is_original_bmesh = false; /* Share various derived caches between the source and destination mesh for improved performance * when the source is persistent and edits to the destination mesh don't affect the caches. * Caches will be "un-shared" as necessary later on. */ mesh_dst->runtime->bounds_cache = mesh_src->runtime->bounds_cache; mesh_dst->runtime->vert_normals_cache = mesh_src->runtime->vert_normals_cache; mesh_dst->runtime->vert_normals_true_cache = mesh_src->runtime->vert_normals_true_cache; mesh_dst->runtime->face_normals_cache = mesh_src->runtime->face_normals_cache; mesh_dst->runtime->face_normals_true_cache = mesh_src->runtime->face_normals_true_cache; mesh_dst->runtime->corner_normals_cache = mesh_src->runtime->corner_normals_cache; mesh_dst->runtime->loose_verts_cache = mesh_src->runtime->loose_verts_cache; mesh_dst->runtime->verts_no_face_cache = mesh_src->runtime->verts_no_face_cache; mesh_dst->runtime->loose_edges_cache = mesh_src->runtime->loose_edges_cache; mesh_dst->runtime->corner_tris_cache = mesh_src->runtime->corner_tris_cache; mesh_dst->runtime->corner_tri_faces_cache = mesh_src->runtime->corner_tri_faces_cache; mesh_dst->runtime->vert_to_face_offset_cache = mesh_src->runtime->vert_to_face_offset_cache; mesh_dst->runtime->vert_to_face_map_cache = mesh_src->runtime->vert_to_face_map_cache; mesh_dst->runtime->vert_to_corner_map_cache = mesh_src->runtime->vert_to_corner_map_cache; mesh_dst->runtime->corner_to_face_map_cache = mesh_src->runtime->corner_to_face_map_cache; mesh_dst->runtime->bvh_cache_verts = mesh_src->runtime->bvh_cache_verts; mesh_dst->runtime->bvh_cache_edges = mesh_src->runtime->bvh_cache_edges; mesh_dst->runtime->bvh_cache_faces = mesh_src->runtime->bvh_cache_faces; mesh_dst->runtime->bvh_cache_corner_tris = mesh_src->runtime->bvh_cache_corner_tris; mesh_dst->runtime->bvh_cache_corner_tris_no_hidden = mesh_src->runtime->bvh_cache_corner_tris_no_hidden; mesh_dst->runtime->bvh_cache_loose_verts = mesh_src->runtime->bvh_cache_loose_verts; mesh_dst->runtime->bvh_cache_loose_verts_no_hidden = mesh_src->runtime->bvh_cache_loose_verts_no_hidden; mesh_dst->runtime->bvh_cache_loose_edges = mesh_src->runtime->bvh_cache_loose_edges; mesh_dst->runtime->bvh_cache_loose_edges_no_hidden = mesh_src->runtime->bvh_cache_loose_edges_no_hidden; mesh_dst->runtime->max_material_index = mesh_src->runtime->max_material_index; if (mesh_src->runtime->bake_materials) { mesh_dst->runtime->bake_materials = std::make_unique( *mesh_src->runtime->bake_materials); } /* Only do tessface if we have no faces. */ const bool do_tessface = ((mesh_src->totface_legacy != 0) && (mesh_src->faces_num == 0)); CustomData_MeshMasks mask = CD_MASK_MESH; if (mesh_src->id.tag & ID_TAG_NO_MAIN) { /* For copies in depsgraph, keep data like #CD_ORIGINDEX and #CD_ORCO. */ CustomData_MeshMasks_update(&mask, &CD_MASK_DERIVEDMESH); /* Meshes copied during evaluation pass the edit mesh pointer to determine whether a mapping * from the evaluated to the original state is possible. */ mesh_dst->runtime->edit_mesh = mesh_src->runtime->edit_mesh; if (const bke::EditMeshData *edit_data = mesh_src->runtime->edit_data.get()) { mesh_dst->runtime->edit_data = std::make_unique(*edit_data); } } mesh_dst->mat = MEM_dupalloc(mesh_src->mat); BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names); mesh_dst->active_color_attribute = static_cast( MEM_dupalloc(mesh_src->active_color_attribute)); mesh_dst->default_color_attribute = static_cast( MEM_dupalloc(mesh_src->default_color_attribute)); mesh_dst->active_uv_map_attribute = static_cast( MEM_dupalloc(mesh_src->active_uv_map_attribute)); mesh_dst->default_uv_map_attribute = static_cast( MEM_dupalloc(mesh_src->default_uv_map_attribute)); mesh_dst->stencil_uv_map_attribute = static_cast( MEM_dupalloc(mesh_src->stencil_uv_map_attribute)); mesh_dst->clone_uv_map_attribute = static_cast( MEM_dupalloc(mesh_src->clone_uv_map_attribute)); CustomData_init_from( &mesh_src->vert_data, &mesh_dst->vert_data, mask.vmask, mesh_dst->verts_num); CustomData_init_from( &mesh_src->edge_data, &mesh_dst->edge_data, mask.emask, mesh_dst->edges_num); CustomData_init_from( &mesh_src->corner_data, &mesh_dst->corner_data, mask.lmask, mesh_dst->corners_num); CustomData_init_from( &mesh_src->face_data, &mesh_dst->face_data, mask.pmask, mesh_dst->faces_num); new (&mesh_dst->attribute_storage.wrap()) bke::AttributeStorage(mesh_src->attribute_storage.wrap()); implicit_sharing::copy_shared_pointer(mesh_src->face_offset_indices, mesh_src->runtime->face_offsets_sharing_info, &mesh_dst->face_offset_indices, &mesh_dst->runtime->face_offsets_sharing_info); if (do_tessface) { CustomData_init_from( &mesh_src->fdata_legacy, &mesh_dst->fdata_legacy, mask.fmask, mesh_dst->totface_legacy); } else { mesh_tessface_clear_intern(mesh_dst, false); } mesh_dst->mselect = MEM_dupalloc(mesh_dst->mselect); if (mesh_src->key && (flag & LIB_ID_COPY_SHAPEKEY)) { BKE_id_copy_in_lib(bmain, owner_library, &mesh_src->key->id, &mesh_dst->id, reinterpret_cast(&mesh_dst->key), flag); /* It has one user, but its owner reference (added in #id_copy_libmanagement_cb) * is the real owner, remove the reference here, see: #159691. */ if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { id_us_min(&mesh_dst->key->id); } } } static void mesh_free_data(ID *id) { Mesh *mesh = reinterpret_cast(id); CustomData_free(&mesh->vert_data); CustomData_free(&mesh->edge_data); CustomData_free(&mesh->fdata_legacy); CustomData_free(&mesh->corner_data); CustomData_free(&mesh->face_data); mesh->vertex_group_names.free_no_destruct(); MEM_SAFE_DELETE(mesh->active_color_attribute); MEM_SAFE_DELETE(mesh->default_color_attribute); MEM_SAFE_DELETE(mesh->active_uv_map_attribute); MEM_SAFE_DELETE(mesh->default_uv_map_attribute); MEM_SAFE_DELETE(mesh->stencil_uv_map_attribute); MEM_SAFE_DELETE(mesh->clone_uv_map_attribute); mesh->attribute_storage.wrap().~AttributeStorage(); if (mesh->face_offset_indices) { implicit_sharing::free_shared_data(&mesh->face_offset_indices, &mesh->runtime->face_offsets_sharing_info); } MEM_SAFE_DELETE(mesh->mselect); MEM_SAFE_DELETE(mesh->mat); delete mesh->runtime; } static void mesh_foreach_id(ID *id, LibraryForeachIDData *data) { Mesh *mesh = reinterpret_cast(id); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->texcomesh, IDWALK_CB_NEVER_SELF); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->key, IDWALK_CB_USER); for (int i = 0; i < mesh->totcol; i++) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->mat[i], IDWALK_CB_USER); } } static void mesh_foreach_path(ID *id, BPathForeachPathData *bpath_data) { Mesh *mesh = reinterpret_cast(id); CustomData &data = mesh->corner_data; if (data.external) { /* CustomDataExternal should only be the case for CD_MDISPS, but check all layers regardless. */ const Span layers(data.layers, data.totlayer); if (std::any_of(layers.begin(), layers.end(), [&](const CustomDataLayer &layer) { return CustomData_external_test(&data, eCustomDataType(layer.type)); })) { BKE_bpath_foreach_path_fixed_process( bpath_data, data.external->filepath, sizeof(data.external->filepath)); } } } static void mesh_foreach_working_space_color(ID *id, const IDTypeForeachColorFunctionCallback &fn) { Mesh *mesh = reinterpret_cast(id); #if 0 /* In the future we'll be able to use just this. */ mesh->attribute_storage.wrap().foreach_working_space_color(fn); #else auto convert_domain = [&fn](CustomData *customdata, size_t size) { for (int i = 0; i < customdata->totlayer; i++) { CustomDataLayer *layer = &customdata->layers[i]; if (layer->data && layer->type == CD_PROP_COLOR) { fn.implicit_sharing_array(*reinterpret_cast *>(&layer->sharing_info), reinterpret_cast(layer->data), size); } } }; convert_domain(&mesh->vert_data, mesh->verts_num); convert_domain(&mesh->edge_data, mesh->edges_num); convert_domain(&mesh->face_data, mesh->faces_num); convert_domain(&mesh->corner_data, mesh->corners_num); #endif } static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address) { using namespace blender::bke; Mesh *mesh = reinterpret_cast(id); const bool is_undo = BLO_write_is_undo(writer); ResourceScope scope; Vector vert_layers; Vector edge_layers; Vector loop_layers; Vector face_layers; bke::AttributeStorage::BlendWriteData attribute_data{writer, scope}; /* Cache only - don't write. */ mesh->mface = nullptr; mesh->totface_legacy = 0; mesh->fdata_legacy = CustomData{}; /* Do not store actual geometry data in case this is a library override ID. */ if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) { mesh->verts_num = 0; mesh->vert_data = CustomData{}; mesh->edges_num = 0; mesh->edge_data = CustomData{}; mesh->corners_num = 0; mesh->corner_data = CustomData{}; mesh->faces_num = 0; mesh->face_data = CustomData{}; mesh->face_offset_indices = nullptr; } else { /* No need to store data in 5.0 data format, because it uses #CustomData at runtime anyway. */ attribute_storage_blend_write_prepare( mesh->attribute_storage.wrap(), false, [&](const AttrDomain domain) { return mesh->attributes().domain_size(domain); }, attribute_data); CustomData_blend_write_prepare(mesh->vert_data, vert_layers); CustomData_blend_write_prepare(mesh->edge_data, edge_layers); CustomData_blend_write_prepare(mesh->face_data, face_layers); CustomData_blend_write_prepare(mesh->corner_data, loop_layers); if (attribute_data.attributes.is_empty()) { mesh->attribute_storage.dna_attributes = nullptr; mesh->attribute_storage.dna_attributes_num = 0; } else { mesh->attribute_storage.dna_attributes = attribute_data.attributes.data(); mesh->attribute_storage.dna_attributes_num = attribute_data.attributes.size(); } } const bke::MeshRuntime *mesh_runtime = mesh->runtime; mesh->runtime = nullptr; BLO_write_generated_pointer_tag(writer, mesh->attribute_storage.dna_attributes); writer->write_id_struct(id_address, mesh); BKE_id_blend_write(writer, &mesh->id); BKE_defbase_blend_write(writer, &mesh->vertex_group_names); writer->write_string(mesh->active_color_attribute); writer->write_string(mesh->default_color_attribute); writer->write_string(mesh->active_uv_map_attribute); writer->write_string(mesh->default_uv_map_attribute); writer->write_string(mesh->stencil_uv_map_attribute); writer->write_string(mesh->clone_uv_map_attribute); writer->write_pointer_array(mesh->totcol, mesh->mat); writer->write_struct_array(mesh->totselect, mesh->mselect); CustomData_blend_write( writer, &mesh->vert_data, vert_layers, mesh->verts_num, CD_MASK_MESH.vmask, &mesh->id); CustomData_blend_write( writer, &mesh->edge_data, edge_layers, mesh->edges_num, CD_MASK_MESH.emask, &mesh->id); /* `fdata` is cleared above but written so slots align. */ CustomData_blend_write( writer, &mesh->fdata_legacy, {}, mesh->totface_legacy, CD_MASK_MESH.fmask, &mesh->id); CustomData_blend_write( writer, &mesh->corner_data, loop_layers, mesh->corners_num, CD_MASK_MESH.lmask, &mesh->id); CustomData_blend_write( writer, &mesh->face_data, face_layers, mesh->faces_num, CD_MASK_MESH.pmask, &mesh->id); mesh->attribute_storage.wrap().blend_write(*writer, attribute_data); if (mesh->face_offset_indices) { BLO_write_shared( writer, mesh->face_offset_indices, sizeof(int) * mesh->faces_num, mesh_runtime->face_offsets_sharing_info, [&]() { writer->write_int32_array(mesh->faces_num + 1, mesh->face_offset_indices); }); } } static void mesh_blend_read_data(BlendDataReader *reader, ID *id) { Mesh *mesh = reinterpret_cast(id); BLO_read_pointer_array_and_validate_size(reader, &mesh->mat, &mesh->totcol); /* This check added for python created meshes. */ if (!mesh->mat) { mesh->totcol = 0; } /* Deprecated pointers to custom data layers are read here for backward compatibility * with files where these were owning pointers rather than a view into custom data. * * Ignore failure to read, these arrays are not further accessed here and blend file * read will abort before versioning runs. */ (void)BLO_read_array(reader, &mesh->mvert, mesh->verts_num); (void)BLO_read_array(reader, &mesh->medge, mesh->edges_num); (void)BLO_read_array(reader, &mesh->mface, mesh->totface_legacy); (void)BLO_read_array(reader, &mesh->mtface, mesh->totface_legacy); (void)BLO_read_array(reader, &mesh->dvert, mesh->verts_num); (void)BLO_read_array(reader, &mesh->tface, mesh->totface_legacy); (void)BLO_read_array(reader, &mesh->mcol, mesh->totface_legacy); BLO_read_array_and_validate_size(reader, &mesh->mselect, &mesh->totselect); BLO_read_struct_list(reader, bDeformGroup, &mesh->vertex_group_names); CustomData_blend_read(reader, &mesh->vert_data, mesh->verts_num); CustomData_blend_read(reader, &mesh->edge_data, mesh->edges_num); CustomData_blend_read(reader, &mesh->fdata_legacy, mesh->totface_legacy); CustomData_blend_read(reader, &mesh->corner_data, mesh->corners_num); CustomData_blend_read(reader, &mesh->face_data, mesh->faces_num); mesh->attribute_storage.wrap().blend_read(*reader); if (mesh->deform_verts().is_empty()) { /* Vertex group data was also an owning pointer in old Blender versions. * Don't read them again if they were read as part of #CustomData. */ BKE_defvert_blend_read(reader, mesh->verts_num, mesh->dvert); } BLO_read_string(reader, &mesh->active_color_attribute); BLO_read_string(reader, &mesh->default_color_attribute); BLO_read_string(reader, &mesh->active_uv_map_attribute); BLO_read_string(reader, &mesh->default_uv_map_attribute); BLO_read_string(reader, &mesh->stencil_uv_map_attribute); BLO_read_string(reader, &mesh->clone_uv_map_attribute); mesh->texspace_flag &= ~ME_TEXSPACE_FLAG_AUTO_EVALUATED; mesh->runtime = new bke::MeshRuntime(); if (mesh->face_offset_indices) { mesh->runtime->face_offsets_sharing_info = BLO_read_shared( reader, &mesh->face_offset_indices, [&]() { if (!BLO_read_array(reader, &mesh->face_offset_indices, int64_t(mesh->faces_num) + 1)) { mesh->faces_num = 0; } return mesh->face_offset_indices ? implicit_sharing::info_for_mem_free(mesh->face_offset_indices) : nullptr; }); } if (mesh->mselect == nullptr) { mesh->totselect = 0; } /* NOTE: this is endianness-sensitive. */ /* Each legacy TFace would need to undo the automatic DNA switch of its array of four uint32_t * RGBA colors. */ } IDTypeInfo IDType_ID_ME = { .id_code = Mesh::id_type, .id_filter = FILTER_ID_ME, .dependencies_id_types = FILTER_ID_ME | FILTER_ID_MA | FILTER_ID_IM | FILTER_ID_KE, .main_listbase_index = INDEX_ID_ME, .struct_size = sizeof(Mesh), .name = "Mesh", .name_plural = N_("meshes"), .translation_context = BLT_I18NCONTEXT_ID_MESH, .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .asset_type_info = nullptr, .init_data = mesh_init_data, .copy_data = mesh_copy_data, .free_data = mesh_free_data, .make_local = nullptr, .foreach_id = mesh_foreach_id, .foreach_cache = nullptr, .foreach_path = mesh_foreach_path, .foreach_working_space_color = mesh_foreach_working_space_color, .owner_pointer_get = nullptr, .blend_write = mesh_blend_write, .blend_read_data = mesh_blend_read_data, .blend_read_after_liblink = nullptr, .blend_read_undo_preserve = nullptr, .lib_override_apply_post = nullptr, }; bool BKE_mesh_attribute_required(const StringRef name) { return ELEM(name, "position", ".corner_vert", ".corner_edge", ".edge_verts"); } void BKE_mesh_ensure_skin_customdata(Mesh *mesh) { BMesh *bm = mesh->runtime->edit_mesh ? mesh->runtime->edit_mesh->bm : nullptr; MVertSkin *vs; if (bm) { if (!CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) { BMVert *v; BMIter iter; BM_data_layer_add(bm, &bm->vdata, CD_MVERT_SKIN); /* Mark an arbitrary vertex as root */ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { vs = static_cast( CustomData_bmesh_get(&bm->vdata, v->head.data, CD_MVERT_SKIN)); vs->flag |= MVERT_SKIN_ROOT; break; } } } else { if (!CustomData_has_layer(&mesh->vert_data, CD_MVERT_SKIN)) { vs = static_cast( CustomData_add_layer(&mesh->vert_data, CD_MVERT_SKIN, CD_SET_DEFAULT, mesh->verts_num)); /* Mark an arbitrary vertex as root */ if (vs) { vs->flag |= MVERT_SKIN_ROOT; } } } } bool BKE_mesh_has_custom_loop_normals(Mesh *mesh) { if (mesh->runtime->edit_mesh) { return CustomData_has_layer_named( &mesh->runtime->edit_mesh->bm->ldata, CD_PROP_INT16_2D, "custom_normal"); } return mesh->attributes().contains("custom_normal"); } namespace bke { void mesh_ensure_default_color_attribute_on_add(Mesh &mesh, const StringRef name, AttrDomain domain, bke::AttrType data_type) { if (bke::attribute_name_is_anonymous(name)) { return; } if (!mesh::is_color_attribute({domain, data_type})) { return; } if (mesh.default_color_attribute) { return; } mesh.default_color_attribute = BLI_strdupn(name.data(), name.size()); } void mesh_ensure_default_uv_attribute_on_add(Mesh &mesh, const StringRef name, AttrDomain domain, bke::AttrType data_type) { if (bke::attribute_name_is_anonymous(name)) { return; } if (!mesh::is_uv_map({domain, data_type})) { return; } if (!mesh.default_uv_map_name().is_empty()) { return; } mesh.uv_maps_default_set(name); } void mesh_ensure_required_data_layers(Mesh &mesh) { MutableAttributeAccessor attributes = mesh.attributes_for_write(); AttributeInitConstruct attribute_init; /* Try to create attributes if they do not exist. */ attributes.add("position", AttrDomain::Point, bke::AttrType::Float3, attribute_init); attributes.add(".edge_verts", AttrDomain::Edge, bke::AttrType::Int32_2D, attribute_init); attributes.add(".corner_vert", AttrDomain::Corner, bke::AttrType::Int32, attribute_init); attributes.add(".corner_edge", AttrDomain::Corner, bke::AttrType::Int32, attribute_init); } static StringRefNull get_first_uv_map_name(const Mesh &mesh) { StringRefNull found; mesh.attributes().foreach_attribute([&](const AttributeIter &iter) { if (iter.domain == AttrDomain::Corner && iter.data_type == AttrType::Float2) { found = iter.name; iter.stop(); } }); return found; } void mesh_ensure_active_uv_map(Mesh &mesh) { const StringRefNull active_name = mesh.active_uv_map_name(); if (!active_name.is_empty()) { return; } const StringRefNull default_name = mesh.default_uv_map_name(); if (!default_name.is_empty()) { mesh.uv_maps_active_set(default_name); return; } const StringRefNull found = get_first_uv_map_name(mesh); if (!found.is_empty()) { mesh.uv_maps_active_set(found); } } void mesh_ensure_default_uv_map(Mesh &mesh) { const StringRefNull default_name = mesh.default_uv_map_name(); if (!default_name.is_empty()) { return; } const StringRefNull found = get_first_uv_map_name(mesh); if (!found.is_empty()) { mesh.uv_maps_default_set(found); } } void mesh_remove_invalid_attribute_strings(Mesh &mesh) { bke::AttributeAccessor attributes = mesh.attributes(); if (!mesh::is_color_attribute(attributes.lookup_meta_data(mesh.active_color_attribute))) { MEM_SAFE_DELETE(mesh.active_color_attribute); } if (!mesh::is_color_attribute(attributes.lookup_meta_data(mesh.default_color_attribute))) { MEM_SAFE_DELETE(mesh.default_color_attribute); } if (!mesh::is_uv_map(attributes.lookup_meta_data(mesh.active_uv_map_name()))) { MEM_SAFE_DELETE(mesh.active_uv_map_attribute); } if (!mesh::is_uv_map(attributes.lookup_meta_data(mesh.default_uv_map_name()))) { MEM_SAFE_DELETE(mesh.default_uv_map_attribute); } if (!mesh::is_uv_map(attributes.lookup_meta_data(mesh.stencil_uv_map_attribute))) { MEM_SAFE_DELETE(mesh.stencil_uv_map_attribute); } if (!mesh::is_uv_map(attributes.lookup_meta_data(mesh.clone_uv_map_attribute))) { MEM_SAFE_DELETE(mesh.clone_uv_map_attribute); } } static Bounds merge_bounds(const Bounds &a, const Bounds &b) { return bounds::merge(a, b); } static Bounds negative_bounds() { return {float3(std::numeric_limits::max()), float3(std::numeric_limits::lowest())}; } struct NonContiguousGroup { Array unique_verts; Array faces; Array shared_verts; int corner_count; int parent; int children_offset; }; static void partition_faces_recursively(const Span face_centers, MutableSpan face_indices, Vector &groups, int node_index, int depth, const std::optional> &bounds_precalc, const Span material_indices, int target_group_size) { if (face_indices.size() <= target_group_size || depth >= STACK_FIXED_DEPTH - 1) { if (!bke::pbvh::leaf_needs_material_split(face_indices, material_indices)) { groups[node_index].children_offset = 0; groups[node_index].faces = Array(face_indices.size(), NoInitialization()); std::copy(face_indices.begin(), face_indices.end(), groups[node_index].faces.begin()); return; } } const int children_start = groups.size(); groups[node_index].children_offset = children_start; groups.resize(groups.size() + 2); groups[children_start].parent = node_index; groups[children_start + 1].parent = node_index; int split; if (!(face_indices.size() <= target_group_size || depth >= STACK_FIXED_DEPTH - 1)) { Bounds bounds; if (bounds_precalc) { bounds = *bounds_precalc; } else { bounds = threading::parallel_reduce( face_indices.index_range(), 1024, negative_bounds(), [&](const IndexRange range, Bounds value) { for (const int face : face_indices.slice(range)) { math::min_max(face_centers[face], value.min, value.max); } return value; }, merge_bounds); } const int axis = math::dominant_axis(bounds.max - bounds.min); split = bke::pbvh::partition_along_axis( face_centers, face_indices, axis, math::midpoint(bounds.min[axis], bounds.max[axis])); } else { split = bke::pbvh::partition_material_indices(material_indices, face_indices); } partition_faces_recursively(face_centers, face_indices.take_front(split), groups, children_start, depth + 1, std::nullopt, material_indices, target_group_size); partition_faces_recursively(face_centers, face_indices.drop_front(split), groups, children_start + 1, depth + 1, std::nullopt, material_indices, target_group_size); } static void build_vertex_groups_for_leaves(const int verts_num, const OffsetIndices faces, const Span corner_verts, Vector &groups) { Vector leaf_indices; for (const int i : groups.index_range()) { if (groups[i].children_offset == 0 && !groups[i].faces.is_empty()) { leaf_indices.append(i); } } Array> verts_per_leaf(leaf_indices.size(), NoInitialization()); threading::parallel_for(leaf_indices.index_range(), 8, [&](const IndexRange range) { Set verts; for (const int i : range) { const int group_idx = leaf_indices[i]; NonContiguousGroup &group = groups[group_idx]; verts.clear(); int corners_count = 0; for (const int face_index : group.faces) { const IndexRange face = faces[face_index]; verts.add_multiple(corner_verts.slice(face)); corners_count += face.size(); } new (&verts_per_leaf[i]) Array(verts.size()); std::copy(verts.begin(), verts.end(), verts_per_leaf[i].begin()); std::ranges::sort(verts_per_leaf[i]); group.corner_count = corners_count; } }); Vector owned_verts; Vector shared_verts; BitVector<> vert_used(verts_num); for (const int i : leaf_indices.index_range()) { const int group_idx = leaf_indices[i]; NonContiguousGroup &group = groups[group_idx]; owned_verts.clear(); shared_verts.clear(); for (const int vert : verts_per_leaf[i]) { if (vert_used[vert]) { shared_verts.append(vert); } else { vert_used[vert].set(); owned_verts.append(vert); } } if (!owned_verts.is_empty()) { group.unique_verts = Array(owned_verts.size()); std::copy(owned_verts.begin(), owned_verts.end(), group.unique_verts.begin()); } if (!shared_verts.is_empty()) { group.shared_verts = Array(shared_verts.size()); std::copy(shared_verts.begin(), shared_verts.end(), group.shared_verts.begin()); } } } static Vector compute_local_mesh_groups(Mesh &mesh) { const Span vert_positions = mesh.vert_positions(); const OffsetIndices faces = mesh.faces(); const Span corner_verts = mesh.corner_verts(); if (faces.is_empty()) { return {}; } Array face_centers(faces.size()); const Bounds bounds = threading::parallel_reduce( faces.index_range(), 1024, negative_bounds(), [&](const IndexRange range, const Bounds &init) { Bounds current = init; for (const int face : range) { const Bounds bounds = bke::pbvh::calc_face_bounds( vert_positions, corner_verts.slice(faces[face])); face_centers[face] = bounds.center(); current = bounds::merge(current, bounds); } return current; }, merge_bounds); Array prim_face_indices(mesh.faces_num); array_utils::fill_index_range(prim_face_indices); Vector groups; groups.resize(1); groups[0].parent = -1; groups[0].children_offset = 0; const AttributeAccessor attributes = mesh.attributes(); const VArraySpan material_index = *attributes.lookup("material_index", AttrDomain::Face); partition_faces_recursively( face_centers, prim_face_indices, groups, 0, 0, bounds, material_index, 2500); build_vertex_groups_for_leaves(mesh.verts_num, faces, corner_verts, groups); return groups; } void mesh_apply_spatial_organization(Mesh &mesh) { BLI_assert(mesh.faces_num != 0); if (mesh.verts_num == 0 || mesh.faces_num == 0) { return; } Vector local_groups = compute_local_mesh_groups(mesh); Vector new_vert_order; new_vert_order.reserve(mesh.verts_num); Vector new_face_order; new_face_order.reserve(mesh.faces_num); BitVector<> added_verts(mesh.verts_num, false); Vector group_unique_offsets; group_unique_offsets.reserve(local_groups.size() + 1); group_unique_offsets.append(0); Vector group_face_offsets; group_face_offsets.reserve(local_groups.size() + 1); group_face_offsets.append(0); for (const int group_index : local_groups.index_range()) { const NonContiguousGroup &local_group = local_groups[group_index]; for (const int vert_idx : local_group.unique_verts) { if (!added_verts[vert_idx]) { new_vert_order.append(vert_idx); added_verts[vert_idx].set(); } } group_unique_offsets.append(new_vert_order.size()); for (const int vert_idx : local_group.shared_verts) { if (!added_verts[vert_idx]) { new_vert_order.append(vert_idx); added_verts[vert_idx].set(); } } for (const int face_idx : local_group.faces) { new_face_order.append(face_idx); } group_face_offsets.append(new_face_order.size()); } for (const int vert : IndexRange(mesh.verts_num)) { if (!added_verts[vert]) { new_vert_order.append(vert); added_verts[vert].set(); } } Array vert_reverse_map(mesh.verts_num); for (const int i : IndexRange(mesh.verts_num)) { vert_reverse_map[new_vert_order[i]] = i; } MutableSpan edges = mesh.edges_for_write(); for (int2 &edge : edges) { edge.x = vert_reverse_map[edge.x]; edge.y = vert_reverse_map[edge.y]; } MutableSpan corner_verts = mesh.corner_verts_for_write(); Array new_corner_verts(corner_verts.size()); const OffsetIndices old_faces = mesh.faces(); int new_corner_idx = 0; for (const int old_face_idx : new_face_order) { const IndexRange face = old_faces[old_face_idx]; for (const int corner : face) { new_corner_verts[new_corner_idx] = vert_reverse_map[corner_verts[corner]]; new_corner_idx++; } } corner_verts.copy_from(new_corner_verts); MutableAttributeAccessor attributes_for_write = mesh.attributes_for_write(); attributes_for_write.foreach_attribute([&](const bke::AttributeIter &iter) { if (iter.storage_type == bke::AttrStorageType::Single) { return; } if (iter.domain == bke::AttrDomain::Face) { bke::GSpanAttributeWriter attribute = attributes_for_write.lookup_for_write_span(iter.name); GMutableSpan attribute_data = attribute.span; const CPPType &type = attribute_data.type(); GArray<> new_values(type, new_face_order.size()); int new_face_idx = 0; for (const int old_face_idx : new_face_order) { type.copy_construct(attribute_data[old_face_idx], new_values[new_face_idx]); new_face_idx++; } attribute_data.copy_from(new_values.as_span()); attribute.finish(); } else if (iter.domain == bke::AttrDomain::Point) { bke::GSpanAttributeWriter attribute = attributes_for_write.lookup_for_write_span(iter.name); GMutableSpan attribute_data = attribute.span; const CPPType &type = attribute_data.type(); GArray<> new_values(type, new_vert_order.size()); int new_vert_idx = 0; for (const int old_vert_idx : new_vert_order) { type.copy_construct(attribute_data[old_vert_idx], new_values[new_vert_idx]); new_vert_idx++; } attribute_data.copy_from(new_values.as_span()); attribute.finish(); } else if (iter.domain == bke::AttrDomain::Corner && iter.name != ".corner_vert") { bke::GSpanAttributeWriter attribute = attributes_for_write.lookup_for_write_span(iter.name); GMutableSpan attribute_data = attribute.span; const CPPType &type = attribute_data.type(); GArray<> new_values(type, attribute_data.size()); int new_corner_idx = 0; for (const int old_face_idx : new_face_order) { const IndexRange face = old_faces[old_face_idx]; for (const int old_corner_idx : face) { type.copy_construct(attribute_data[old_corner_idx], new_values[new_corner_idx]); new_corner_idx++; } } attribute_data.copy_from(new_values.as_span()); attribute.finish(); } }); Vector face_sizes(new_face_order.size()); gather_group_sizes(old_faces, new_face_order, face_sizes); MutableSpan face_offsets = mesh.face_offsets_for_write(); face_offsets.take_front(face_sizes.size()).copy_from(face_sizes); offset_indices::accumulate_counts_to_offsets(face_offsets); for (NonContiguousGroup &local_group : local_groups) { for (int &vert_idx : local_group.unique_verts) { vert_idx = vert_reverse_map[vert_idx]; } for (int &vert_idx : local_group.shared_verts) { vert_idx = vert_reverse_map[vert_idx]; } } Array nodes(local_groups.size()); for (const int node_idx : local_groups.index_range()) { const NonContiguousGroup &local_group = local_groups[node_idx]; MeshGroup &node = nodes[node_idx]; node.parent = local_group.parent; node.children_offset = local_group.children_offset; node.corners_count = local_group.corner_count; node.unique_verts = IndexRange(0, 0); node.faces = IndexRange(0, 0); if (local_group.children_offset == 0 && !local_group.faces.is_empty()) { int unique_start = (node_idx == 0) ? 0 : group_unique_offsets[node_idx]; int unique_end = group_unique_offsets[node_idx + 1]; node.unique_verts = IndexRange(unique_start, unique_end - unique_start); int face_start = (node_idx == 0) ? 0 : group_face_offsets[node_idx]; int face_end = group_face_offsets[node_idx + 1]; node.faces = IndexRange(face_start, face_end - face_start); if (!local_group.shared_verts.is_empty()) { node.shared_verts = Array(local_group.shared_verts.size()); for (const int j : local_group.shared_verts.index_range()) { node.shared_verts[j] = local_group.shared_verts[j]; } } } } mesh.tag_positions_changed(); mesh.tag_topology_changed(); mesh.runtime->spatial_groups = std::make_unique>(std::move(nodes)); } } // namespace bke /** * \note on data that this function intentionally doesn't free: * * - Materials and shape keys are not freed here (#Mesh.mat & #Mesh.key). * As freeing shape keys requires tagging the depsgraph for updated relations, * which is expensive. * Material slots should be kept in sync with the object. * * - Edit-Mesh (#Mesh.edit_mesh) * Since edit-mesh is tied to the object's mode, which crashes when called in edit-mode. * See: #90972. */ static void mesh_clear_geometry(Mesh &mesh) { CustomData_free(&mesh.vert_data); CustomData_free(&mesh.edge_data); CustomData_free(&mesh.fdata_legacy); CustomData_free(&mesh.corner_data); CustomData_free(&mesh.face_data); mesh.attribute_storage.wrap() = bke::AttributeStorage(); if (mesh.face_offset_indices) { implicit_sharing::free_shared_data(&mesh.face_offset_indices, &mesh.runtime->face_offsets_sharing_info); } MEM_SAFE_DELETE(mesh.mselect); mesh.verts_num = 0; mesh.edges_num = 0; mesh.totface_legacy = 0; mesh.corners_num = 0; mesh.faces_num = 0; mesh.act_face = -1; mesh.totselect = 0; } static void clear_attribute_names(Mesh &mesh) { mesh.vertex_group_names.free_no_destruct(); MEM_SAFE_DELETE(mesh.active_color_attribute); MEM_SAFE_DELETE(mesh.default_color_attribute); MEM_SAFE_DELETE(mesh.active_uv_map_attribute); MEM_SAFE_DELETE(mesh.default_uv_map_attribute); MEM_SAFE_DELETE(mesh.stencil_uv_map_attribute); MEM_SAFE_DELETE(mesh.clone_uv_map_attribute); } void BKE_mesh_clear_geometry(Mesh *mesh) { BKE_mesh_runtime_clear_cache(mesh); mesh_clear_geometry(*mesh); } void BKE_mesh_clear_geometry_and_metadata(Mesh *mesh) { BKE_mesh_runtime_clear_cache(mesh); mesh_clear_geometry(*mesh); clear_attribute_names(*mesh); } static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata) { if (free_customdata) { CustomData_free(&mesh->fdata_legacy); } else { CustomData_reset(&mesh->fdata_legacy); } mesh->totface_legacy = 0; } Mesh *BKE_mesh_add(Main *bmain, const char *name) { return BKE_id_new(bmain, name); } void BKE_mesh_face_offsets_ensure_alloc(Mesh *mesh) { BLI_assert(mesh->face_offset_indices == nullptr); BLI_assert(mesh->runtime->face_offsets_sharing_info == nullptr); if (mesh->faces_num == 0) { return; } mesh->face_offset_indices = MEM_new_array_uninitialized(size_t(mesh->faces_num) + 1, __func__); mesh->runtime->face_offsets_sharing_info = implicit_sharing::info_for_mem_free( mesh->face_offset_indices); #ifndef NDEBUG /* Fill offsets with obviously bad values to simplify finding missing initialization. */ mesh->face_offsets_for_write().fill(-1); #endif /* Set common values for convenience. */ mesh->face_offset_indices[0] = 0; mesh->face_offset_indices[mesh->faces_num] = mesh->corners_num; } Span Mesh::vert_positions() const { return bke::get_span_attribute( this->attribute_storage.wrap(), bke::AttrDomain::Point, "position", this->verts_num) .value_or(Span()); } MutableSpan Mesh::vert_positions_for_write() { return bke::get_mutable_attribute( this->attribute_storage.wrap(), bke::AttrDomain::Point, "position", this->verts_num); } Span Mesh::edges() const { return bke::get_span_attribute( this->attribute_storage.wrap(), bke::AttrDomain::Edge, ".edge_verts", this->edges_num) .value_or(Span()); } MutableSpan Mesh::edges_for_write() { return bke::get_mutable_attribute( this->attribute_storage.wrap(), bke::AttrDomain::Edge, ".edge_verts", this->edges_num); } OffsetIndices Mesh::faces() const { return Span(this->face_offset_indices, this->faces_num + 1); } Span Mesh::face_offsets() const { if (this->faces_num == 0) { return {}; } return {this->face_offset_indices, this->faces_num + 1}; } MutableSpan Mesh::face_offsets_for_write() { if (this->faces_num == 0) { return {}; } implicit_sharing::make_trivial_data_mutable( &this->face_offset_indices, &this->runtime->face_offsets_sharing_info, this->faces_num + 1); return {this->face_offset_indices, this->faces_num + 1}; } Span Mesh::corner_verts() const { return bke::get_span_attribute(this->attribute_storage.wrap(), bke::AttrDomain::Corner, ".corner_vert", this->corners_num) .value_or(Span()); } MutableSpan Mesh::corner_verts_for_write() { return bke::get_mutable_attribute( this->attribute_storage.wrap(), bke::AttrDomain::Corner, ".corner_vert", this->corners_num); } Span Mesh::corner_edges() const { return bke::get_span_attribute(this->attribute_storage.wrap(), bke::AttrDomain::Corner, ".corner_edge", this->corners_num) .value_or(Span()); } MutableSpan Mesh::corner_edges_for_write() { return bke::get_mutable_attribute( this->attribute_storage.wrap(), bke::AttrDomain::Corner, ".corner_edge", this->corners_num); } Span Mesh::deform_verts() const { const MDeformVert *dverts = static_cast( CustomData_get_layer(&this->vert_data, CD_MDEFORMVERT)); if (!dverts) { return {}; } return {dverts, this->verts_num}; } MutableSpan Mesh::deform_verts_for_write() { MDeformVert *dvert = static_cast( CustomData_get_layer_for_write(&this->vert_data, CD_MDEFORMVERT, this->verts_num)); if (dvert) { return {dvert, this->verts_num}; } return {static_cast(CustomData_add_layer( &this->vert_data, CD_MDEFORMVERT, CD_SET_DEFAULT, this->verts_num)), this->verts_num}; } void Mesh::count_memory(MemoryCounter &memory) const { memory.add_shared(this->runtime->face_offsets_sharing_info, this->face_offsets().size_in_bytes()); this->attribute_storage.wrap().count_memory(memory); CustomData_count_memory(this->vert_data, this->verts_num, memory); CustomData_count_memory(this->edge_data, this->edges_num, memory); CustomData_count_memory(this->face_data, this->faces_num, memory); CustomData_count_memory(this->corner_data, this->corners_num, memory); } bke::AttributeAccessor Mesh::attributes() const { return bke::AttributeAccessor(this, bke::mesh_attribute_accessor_functions()); } bke::MutableAttributeAccessor Mesh::attributes_for_write() { return bke::MutableAttributeAccessor(this, bke::mesh_attribute_accessor_functions()); } VectorSet Mesh::uv_map_names() const { VectorSet names; this->attributes().foreach_attribute([&](const bke::AttributeIter &iter) { if (bke::mesh::is_uv_map({iter.domain, iter.data_type})) { names.add_new(iter.name); } }); return names; } StringRefNull Mesh::active_uv_map_name() const { if (BMEditMesh *em = this->runtime->edit_mesh.get()) { const char *name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2); return name ? name : ""; } return this->active_uv_map_attribute ? this->active_uv_map_attribute : ""; } StringRefNull Mesh::default_uv_map_name() const { if (BMEditMesh *em = this->runtime->edit_mesh.get()) { const char *name = CustomData_get_render_layer_name(&em->bm->ldata, CD_PROP_FLOAT2); return name ? name : ""; } return this->default_uv_map_attribute ? this->default_uv_map_attribute : ""; } StringRefNull Mesh::active_or_default_uv_map_name() const { const StringRefNull active_name = this->active_uv_map_name(); if (!active_name.is_empty()) { return active_name; } return this->default_uv_map_name(); } void Mesh::uv_maps_active_set(const StringRef name) { MEM_SAFE_DELETE(this->active_uv_map_attribute); if (!name.is_empty()) { this->active_uv_map_attribute = BLI_strdupn(name.data(), name.size()); } if (BMEditMesh *em = this->runtime->edit_mesh.get()) { int index = CustomData_get_named_layer_index(&em->bm->ldata, CD_PROP_FLOAT2, name); if (index == -1) { index = CustomData_get_layer_index(&em->bm->ldata, CD_PROP_FLOAT2); } CustomData_set_layer_active_index(&em->bm->ldata, CD_PROP_FLOAT2, index); } } void Mesh::uv_maps_default_set(const StringRef name) { MEM_SAFE_DELETE(this->default_uv_map_attribute); if (!name.is_empty()) { this->default_uv_map_attribute = BLI_strdupn(name.data(), name.size()); } if (BMEditMesh *em = this->runtime->edit_mesh.get()) { int index = CustomData_get_named_layer_index(&em->bm->ldata, CD_PROP_FLOAT2, name); if (index == -1) { index = CustomData_get_layer_index(&em->bm->ldata, CD_PROP_FLOAT2); } CustomData_set_layer_render_index(&em->bm->ldata, CD_PROP_FLOAT2, index); } } Mesh *BKE_mesh_new_nomain(const int verts_num, const int edges_num, const int faces_num, const int corners_num) { Mesh *mesh = bke::mesh_new_no_attributes(verts_num, edges_num, faces_num, corners_num); bke::mesh_ensure_required_data_layers(*mesh); return mesh; } namespace bke { namespace mesh { bool is_uv_map(const AttributeMetaData &meta_data) { return meta_data.domain == AttrDomain::Corner && meta_data.data_type == AttrType::Float2; } bool is_uv_map(const std::optional &meta_data) { return meta_data && is_uv_map(*meta_data); } bool is_color_attribute(const bke::AttributeMetaData &meta_data) { return ELEM(meta_data.domain, bke::AttrDomain::Point, bke::AttrDomain::Corner) && ELEM(meta_data.data_type, bke::AttrType::ColorByte, bke::AttrType::ColorFloat); } bool is_color_attribute(const std::optional &meta_data) { return meta_data && is_color_attribute(*meta_data); } } // namespace mesh Mesh *mesh_new_no_attributes(const int verts_num, const int edges_num, const int faces_num, const int corners_num) { Mesh *mesh = static_cast(BKE_libblock_alloc( nullptr, ID_ME, BKE_idtype_idcode_to_name(ID_ME), LIB_ID_CREATE_LOCALIZE)); BKE_libblock_init_empty(&mesh->id); mesh->verts_num = verts_num; mesh->edges_num = edges_num; mesh->faces_num = faces_num; mesh->corners_num = corners_num; BKE_mesh_face_offsets_ensure_alloc(mesh); return mesh; } } // namespace bke static void copy_attribute_names(const Mesh &mesh_src, Mesh &mesh_dst) { if (mesh_src.active_color_attribute) { MEM_SAFE_DELETE(mesh_dst.active_color_attribute); mesh_dst.active_color_attribute = BLI_strdup(mesh_src.active_color_attribute); } if (mesh_src.default_color_attribute) { MEM_SAFE_DELETE(mesh_dst.default_color_attribute); mesh_dst.default_color_attribute = BLI_strdup(mesh_src.default_color_attribute); } if (mesh_src.active_uv_map_attribute) { MEM_SAFE_DELETE(mesh_dst.active_uv_map_attribute); mesh_dst.active_uv_map_attribute = BLI_strdup(mesh_src.active_uv_map_attribute); } if (mesh_src.default_uv_map_attribute) { MEM_SAFE_DELETE(mesh_dst.default_uv_map_attribute); mesh_dst.default_uv_map_attribute = BLI_strdup(mesh_src.default_uv_map_attribute); } if (mesh_src.stencil_uv_map_attribute) { MEM_SAFE_DELETE(mesh_dst.stencil_uv_map_attribute); mesh_dst.stencil_uv_map_attribute = BLI_strdup(mesh_src.stencil_uv_map_attribute); } if (mesh_src.clone_uv_map_attribute) { MEM_SAFE_DELETE(mesh_dst.clone_uv_map_attribute); mesh_dst.clone_uv_map_attribute = BLI_strdup(mesh_src.clone_uv_map_attribute); } } void BKE_mesh_copy_parameters(Mesh *me_dst, const Mesh *me_src) { /* Copy general settings. */ me_dst->editflag = me_src->editflag; me_dst->flag = me_src->flag; me_dst->remesh_voxel_size = me_src->remesh_voxel_size; me_dst->remesh_voxel_adaptivity = me_src->remesh_voxel_adaptivity; me_dst->remesh_mode = me_src->remesh_mode; me_dst->symmetry = me_src->symmetry; me_dst->face_sets_color_seed = me_src->face_sets_color_seed; me_dst->face_sets_color_default = me_src->face_sets_color_default; /* Copy texture space. */ me_dst->texspace_flag = me_src->texspace_flag; copy_v3_v3(me_dst->texspace_location, me_src->texspace_location); copy_v3_v3(me_dst->texspace_size, me_src->texspace_size); me_dst->vertex_group_active_index = me_src->vertex_group_active_index; me_dst->attributes_active_index = me_src->attributes_active_index; } void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src) { /* User counts aren't handled, don't copy into a mesh from #G_MAIN. */ BLI_assert(me_dst->id.tag & (ID_TAG_NO_MAIN | ID_TAG_COPIED_ON_EVAL)); STRNCPY(me_dst->id.name, me_src->id.name); BKE_mesh_copy_parameters(me_dst, me_src); copy_attribute_names(*me_src, *me_dst); /* Copy vertex group names. */ BLI_assert(me_dst->vertex_group_names.is_empty()); BKE_defgroup_copy_list(&me_dst->vertex_group_names, &me_src->vertex_group_names); /* Copy materials. */ if (me_dst->mat != nullptr) { MEM_delete(me_dst->mat); } me_dst->mat = MEM_dupalloc(me_src->mat); me_dst->totcol = me_src->totcol; me_dst->runtime->edit_mesh = me_src->runtime->edit_mesh; } Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src, const int verts_num, const int edges_num, const int tessface_num, const int faces_num, const int corners_num, const CustomData_MeshMasks mask) { /* Only do tessface if we are creating tessfaces or copying from mesh with only tessfaces. */ const bool do_tessface = (tessface_num || ((me_src->totface_legacy != 0) && (me_src->faces_num == 0))); Mesh *me_dst = BKE_id_new_nomain(nullptr); STRNCPY(me_dst->id.name, me_src->id.name); me_dst->mselect = MEM_dupalloc(me_src->mselect); me_dst->verts_num = verts_num; me_dst->edges_num = edges_num; me_dst->faces_num = faces_num; me_dst->corners_num = corners_num; me_dst->totface_legacy = tessface_num; BKE_mesh_copy_parameters_for_eval(me_dst, me_src); CustomData_init_layout_from( &me_src->vert_data, &me_dst->vert_data, mask.vmask, CD_SET_DEFAULT, verts_num); CustomData_init_layout_from( &me_src->edge_data, &me_dst->edge_data, mask.emask, CD_SET_DEFAULT, edges_num); CustomData_init_layout_from( &me_src->face_data, &me_dst->face_data, mask.pmask, CD_SET_DEFAULT, faces_num); CustomData_init_layout_from( &me_src->corner_data, &me_dst->corner_data, mask.lmask, CD_SET_DEFAULT, corners_num); if (do_tessface) { CustomData_init_layout_from( &me_src->fdata_legacy, &me_dst->fdata_legacy, mask.fmask, CD_SET_DEFAULT, tessface_num); } else { mesh_tessface_clear_intern(me_dst, false); } /* The destination mesh should at least have valid primary CD layers, * even in cases where the source mesh does not. */ bke::mesh_ensure_required_data_layers(*me_dst); BKE_mesh_face_offsets_ensure_alloc(me_dst); if (do_tessface && !CustomData_get_layer(&me_dst->fdata_legacy, CD_MFACE)) { CustomData_add_layer(&me_dst->fdata_legacy, CD_MFACE, CD_SET_DEFAULT, me_dst->totface_legacy); } bke::MutableAttributeAccessor dst_attrs = me_dst->attributes_for_write(); for (const bke::Attribute &attr : me_src->attribute_storage.wrap()) { if (dst_attrs.contains(attr.name())) { continue; } dst_attrs.add(attr.name(), attr.domain(), attr.data_type(), bke::AttributeInitDefaultValue()); } return me_dst; } Mesh *BKE_mesh_new_nomain_from_template(const Mesh *me_src, const int verts_num, const int edges_num, const int faces_num, const int corners_num) { return BKE_mesh_new_nomain_from_template_ex( me_src, verts_num, edges_num, 0, faces_num, corners_num, CD_MASK_EVERYTHING); } Mesh *BKE_mesh_copy_for_eval(const Mesh &source) { return reinterpret_cast( BKE_id_copy_ex(nullptr, &source.id, nullptr, LIB_ID_COPY_LOCALIZE)); } BMesh *BKE_mesh_to_bmesh_ex(const Mesh *mesh, const BMeshCreateParams *create_params, const BMeshFromMeshParams *convert_params) { const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); BMesh *bm = BM_mesh_create(&allocsize, create_params); BM_mesh_bm_from_me(bm, mesh, convert_params); return bm; } BMesh *BKE_mesh_to_bmesh(Mesh *mesh, const int active_shapekey, const bool add_key_index, const BMeshCreateParams *params) { BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = false; bmesh_from_mesh_params.calc_vert_normal = false; bmesh_from_mesh_params.add_key_index = add_key_index; bmesh_from_mesh_params.use_shapekey = true; bmesh_from_mesh_params.active_shapekey = active_shapekey; return BKE_mesh_to_bmesh_ex(mesh, params, &bmesh_from_mesh_params); } Mesh *BKE_mesh_from_bmesh_nomain(BMesh *bm, const BMeshToMeshParams *params, const Mesh *me_settings) { BLI_assert(params->calc_object_remap == false); Mesh *mesh = BKE_id_new_nomain(nullptr); BM_mesh_bm_to_me(nullptr, bm, mesh, params); BKE_mesh_copy_parameters_for_eval(mesh, me_settings); return mesh; } Mesh *BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, const CustomData_MeshMasks *cd_mask_extra, const Mesh *me_settings) { Mesh *mesh = BKE_id_new_nomain(nullptr); BM_mesh_bm_to_me_for_eval(*bm, *mesh, cd_mask_extra); BKE_mesh_copy_parameters_for_eval(mesh, me_settings); return mesh; } static void ensure_orig_index_layer(CustomData &data, const int size) { if (CustomData_has_layer(&data, CD_ORIGINDEX)) { return; } int *indices = static_cast( CustomData_add_layer(&data, CD_ORIGINDEX, CD_SET_DEFAULT, size)); range_vn_i(indices, size, 0); } void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh) { BLI_assert(mesh->runtime->wrapper_type == ME_WRAPPER_TYPE_MDATA); BKE_mesh_ensure_default_orig_index_customdata_no_check(mesh); } void BKE_mesh_ensure_default_orig_index_customdata_no_check(Mesh *mesh) { ensure_orig_index_layer(mesh->vert_data, mesh->verts_num); ensure_orig_index_layer(mesh->edge_data, mesh->edges_num); ensure_orig_index_layer(mesh->face_data, mesh->faces_num); } void BKE_mesh_texspace_calc(Mesh *mesh) { if (mesh->texspace_flag & ME_TEXSPACE_FLAG_AUTO) { const Bounds bounds = mesh->bounds_min_max().value_or( Bounds(float3(-1.0f), float3(1.0f))); float texspace_location[3], texspace_size[3]; mid_v3_v3v3(texspace_location, bounds.min, bounds.max); texspace_size[0] = (bounds.max[0] - bounds.min[0]) / 2.0f; texspace_size[1] = (bounds.max[1] - bounds.min[1]) / 2.0f; texspace_size[2] = (bounds.max[2] - bounds.min[2]) / 2.0f; for (int a = 0; a < 3; a++) { if (texspace_size[a] == 0.0f) { texspace_size[a] = 1.0f; } else if (texspace_size[a] > 0.0f && texspace_size[a] < 0.00001f) { texspace_size[a] = 0.00001f; } else if (texspace_size[a] < 0.0f && texspace_size[a] > -0.00001f) { texspace_size[a] = -0.00001f; } } copy_v3_v3(mesh->texspace_location, texspace_location); copy_v3_v3(mesh->texspace_size, texspace_size); mesh->texspace_flag |= ME_TEXSPACE_FLAG_AUTO_EVALUATED; } } void BKE_mesh_texspace_ensure(Mesh *mesh) { if ((mesh->texspace_flag & ME_TEXSPACE_FLAG_AUTO) && !(mesh->texspace_flag & ME_TEXSPACE_FLAG_AUTO_EVALUATED)) { BKE_mesh_texspace_calc(mesh); } } void BKE_mesh_texspace_get(Mesh *mesh, float r_texspace_location[3], float r_texspace_size[3]) { BKE_mesh_texspace_ensure(mesh); if (r_texspace_location) { copy_v3_v3(r_texspace_location, mesh->texspace_location); } if (r_texspace_size) { copy_v3_v3(r_texspace_size, mesh->texspace_size); } } void BKE_mesh_texspace_get_reference(Mesh *mesh, char **r_texspace_flag, float **r_texspace_location, float **r_texspace_size) { BKE_mesh_texspace_ensure(mesh); if (r_texspace_flag != nullptr) { *r_texspace_flag = &mesh->texspace_flag; } if (r_texspace_location != nullptr) { *r_texspace_location = mesh->texspace_location; } if (r_texspace_size != nullptr) { *r_texspace_size = mesh->texspace_size; } } Array BKE_mesh_orco_verts_get(const Object *ob) { const Mesh *mesh = id_cast(ob->data); const Mesh *tme = mesh->texcomesh ? mesh->texcomesh : mesh; Array result(mesh->verts_num); const Span positions = tme->vert_positions(); result.as_mutable_span().take_front(positions.size()).copy_from(positions); result.as_mutable_span().drop_front(positions.size()).fill(float3(0)); return result; } void BKE_mesh_orco_verts_transform(Mesh *mesh, MutableSpan orco, const bool invert) { float texspace_location[3], texspace_size[3]; BKE_mesh_texspace_get( mesh->texcomesh ? mesh->texcomesh : mesh, texspace_location, texspace_size); if (invert) { for (const int a : orco.index_range()) { float3 &co = orco[a]; madd_v3_v3v3v3(co, texspace_location, co, texspace_size); } } else { for (const int a : orco.index_range()) { float3 &co = orco[a]; co[0] = (co[0] - texspace_location[0]) / texspace_size[0]; co[1] = (co[1] - texspace_location[1]) / texspace_size[1]; co[2] = (co[2] - texspace_location[2]) / texspace_size[2]; } } } void BKE_mesh_orco_verts_transform(Mesh *mesh, float (*orco)[3], int totvert, bool invert) { BKE_mesh_orco_verts_transform(mesh, {reinterpret_cast(orco), totvert}, invert); } void BKE_mesh_orco_ensure(Object *ob, Mesh *mesh) { if (CustomData_has_layer(&mesh->vert_data, CD_ORCO)) { return; } /* Orcos are stored in normalized 0..1 range by convention. */ Array orcodata = BKE_mesh_orco_verts_get(ob); BKE_mesh_orco_verts_transform(mesh, orcodata, false); float3 *data = static_cast( CustomData_add_layer(&mesh->vert_data, CD_ORCO, CD_CONSTRUCT, mesh->verts_num)); MutableSpan(data, mesh->verts_num).copy_from(orcodata); } Mesh *BKE_mesh_from_object(Object *ob) { if (ob == nullptr) { return nullptr; } if (ob->type == OB_MESH) { return id_cast(ob->data); } return nullptr; } void BKE_mesh_assign_object(Main *bmain, Object *ob, Mesh *mesh) { ID *old = nullptr; if (ob == nullptr) { return; } multires_force_sculpt_rebuild(ob); if (ob->type == OB_MESH) { old = ob->data; if (old) { id_us_min(old); } ob->data = id_cast(mesh); id_us_plus(ob->data); } BKE_object_materials_sync_length(bmain, ob, id_cast(mesh)); BKE_modifiers_test_object(ob); } void BKE_mesh_material_index_remove(Mesh *mesh, short index) { using namespace blender::bke; MutableAttributeAccessor attributes = mesh->attributes_for_write(); AttributeWriter material_indices = attributes.lookup_for_write("material_index"); if (!material_indices) { return; } if (material_indices.domain != AttrDomain::Face) { BLI_assert_unreachable(); return; } MutableVArraySpan indices_span(material_indices.varray); for (const int i : indices_span.index_range()) { if (indices_span[i] > 0 && indices_span[i] >= index) { indices_span[i]--; } } indices_span.save(); material_indices.finish(); BKE_mesh_tessface_clear(mesh); } bool BKE_mesh_material_index_used(Mesh *mesh, short index) { using namespace blender::bke; const AttributeAccessor attributes = mesh->attributes(); const VArray material_indices = *attributes.lookup_or_default( "material_index", AttrDomain::Face, 0); if (material_indices.is_single()) { return material_indices.get_internal_single() == index; } const VArraySpan indices_span(material_indices); return indices_span.contains(index); } void BKE_mesh_material_index_clear(Mesh *mesh) { using namespace blender::bke; MutableAttributeAccessor attributes = mesh->attributes_for_write(); attributes.remove("material_index"); BKE_mesh_tessface_clear(mesh); } void BKE_mesh_material_remap(Mesh *mesh, const uint *remap, uint remap_len) { using namespace blender::bke; const short remap_len_short = short(remap_len); #define MAT_NR_REMAP(n) \ if (n < remap_len_short) { \ BLI_assert(n >= 0 && remap[n] < remap_len_short); \ n = remap[n]; \ } \ ((void)0) if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) { BMIter iter; BMFace *efa; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { MAT_NR_REMAP(efa->mat_nr); } } else { MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter material_indices = attributes.lookup_or_add_for_write_span( "material_index", AttrDomain::Face); if (!material_indices) { return; } for (const int i : material_indices.span.index_range()) { MAT_NR_REMAP(material_indices.span[i]); } material_indices.span.save(); material_indices.finish(); } #undef MAT_NR_REMAP } namespace bke { void mesh_smooth_set(Mesh &mesh, const bool use_smooth, const bool keep_sharp_edges) { MutableAttributeAccessor attributes = mesh.attributes_for_write(); if (!keep_sharp_edges) { attributes.remove("sharp_edge"); } attributes.remove("sharp_face"); if (!use_smooth) { attributes.add("sharp_face", AttrDomain::Face, AttributeInitValue(true)); } } void mesh_sharp_edges_set_from_angle(Mesh &mesh, const float angle, const bool keep_sharp_edges) { MutableAttributeAccessor attributes = mesh.attributes_for_write(); if (angle >= M_PI) { mesh_smooth_set(mesh, true, keep_sharp_edges); return; } if (angle == 0.0f) { mesh_smooth_set(mesh, false, keep_sharp_edges); return; } if (!keep_sharp_edges) { attributes.remove("sharp_edge"); } SpanAttributeWriter sharp_edges = attributes.lookup_or_add_for_write_span( "sharp_edge", AttrDomain::Edge); const VArraySpan sharp_faces = *attributes.lookup("sharp_face", AttrDomain::Face); mesh::edges_sharp_from_angle_set(mesh.faces(), mesh.corner_verts(), mesh.corner_edges(), mesh.face_normals(), mesh.corner_to_face_map(), sharp_faces, angle, sharp_edges.span); sharp_edges.finish(); } } // namespace bke std::optional> Mesh::bounds_min_max() const { const int verts_num = BKE_mesh_wrapper_vert_len(this); if (verts_num == 0) { return std::nullopt; } this->runtime->bounds_cache.ensure([&](Bounds &r_bounds) { switch (this->runtime->wrapper_type) { case ME_WRAPPER_TYPE_BMESH: r_bounds = *BKE_editmesh_cache_calc_minmax(*this->runtime->edit_mesh, *this->runtime->edit_data); break; case ME_WRAPPER_TYPE_MDATA: case ME_WRAPPER_TYPE_SUBD: r_bounds = *bounds::min_max(this->vert_positions()); break; } }); return this->runtime->bounds_cache.data(); } void Mesh::bounds_set_eager(const Bounds &bounds) { this->runtime->bounds_cache.ensure([&](Bounds &r_data) { r_data = bounds; }); } static bool use_bmesh_material_indices(const Mesh &mesh) { return mesh.runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH && mesh.runtime->edit_mesh && mesh.runtime->edit_mesh->bm; } std::optional Mesh::material_index_max() const { this->runtime->max_material_index.ensure([&](std::optional &value) { if (use_bmesh_material_indices(*this)) { BMesh *bm = this->runtime->edit_mesh->bm; if (bm->totface == 0) { value = std::nullopt; return; } int max_material_index = 0; BMFace *efa; BMIter iter; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { max_material_index = std::max(max_material_index, efa->mat_nr); } value = max_material_index; return; } if (this->faces_num == 0) { value = std::nullopt; return; } value = bounds::max( *this->attributes().lookup_or_default("material_index", bke::AttrDomain::Face, 0)); if (value.has_value()) { value = std::clamp(*value, 0, MAXMAT); } }); return this->runtime->max_material_index.data(); } const VectorSet &Mesh::material_indices_used() const { this->runtime->used_material_indices.ensure([&](VectorSet &r_data) { const std::optional max_material_index_opt = this->material_index_max(); r_data.clear(); if (!max_material_index_opt.has_value()) { return; } const int max_material_index = *max_material_index_opt; const auto clamp_material_index = [&](const int index) { return std::clamp(index, 0, max_material_index); }; /* Find used indices in parallel and then create the vector set in the end. */ Array used_indices(max_material_index + 1, false); if (use_bmesh_material_indices(*this)) { BMesh *bm = this->runtime->edit_mesh->bm; BMFace *efa; BMIter iter; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { used_indices[clamp_material_index(efa->mat_nr)] = true; } } else if (const VArray material_indices = *this->attributes().lookup_or_default( "material_index", bke::AttrDomain::Face, 0)) { if (const std::optional single_material_index = material_indices.get_if_single()) { used_indices[clamp_material_index(*single_material_index)] = true; } else { VArraySpan material_indices_span = material_indices; threading::parallel_for( material_indices_span.index_range(), 8096, [&](const IndexRange range) { for (const int i : range) { used_indices[clamp_material_index(material_indices_span[i])] = true; } }); } } for (const int i : used_indices.index_range()) { if (used_indices[i]) { r_data.add_new(i); } } }); return this->runtime->used_material_indices.data(); } namespace bke { static void translate_positions(MutableSpan positions, const float3 &translation) { threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) { for (float3 &position : positions.slice(range)) { position += translation; } }); } void mesh_translate(Mesh &mesh, const float3 &translation, const bool do_shape_keys) { if (math::is_zero(translation)) { return; } std::optional> bounds; if (mesh.runtime->bounds_cache.is_cached()) { bounds = mesh.runtime->bounds_cache.data(); } translate_positions(mesh.vert_positions_for_write(), translation); if (do_shape_keys && mesh.key) { for (KeyBlock &kb : mesh.key->block) { translate_positions({static_cast(kb.data), kb.totelem}, translation); } } mesh.tag_positions_changed_uniformly(); if (bounds) { bounds->min += translation; bounds->max += translation; mesh.bounds_set_eager(*bounds); } } void mesh_transform(Mesh &mesh, const float4x4 &transform, bool do_shape_keys) { math::transform_points(transform, mesh.vert_positions_for_write()); if (do_shape_keys && mesh.key) { for (KeyBlock &kb : mesh.key->block) { math::transform_points(transform, MutableSpan(static_cast(kb.data), kb.totelem)); } } MutableAttributeAccessor attributes = mesh.attributes_for_write(); transform_custom_normal_attribute(transform, attributes); mesh.tag_positions_changed(); } } // namespace bke void BKE_mesh_tessface_clear(Mesh *mesh) { mesh_tessface_clear_intern(mesh, true); } /* -------------------------------------------------------------------- */ /* MSelect functions (currently used in weight paint mode) */ void BKE_mesh_mselect_clear(Mesh *mesh) { MEM_SAFE_DELETE(mesh->mselect); mesh->totselect = 0; } void BKE_mesh_mselect_validate(Mesh *mesh) { using namespace blender::bke; MSelect *mselect_src, *mselect_dst; int i_src, i_dst; if (mesh->totselect == 0) { return; } mselect_src = mesh->mselect; mselect_dst = MEM_new_array_uninitialized(size_t(mesh->totselect), "Mesh selection history"); const AttributeAccessor attributes = mesh->attributes(); const VArray select_vert = *attributes.lookup_or_default( ".select_vert", AttrDomain::Point, false); const VArray select_edge = *attributes.lookup_or_default( ".select_edge", AttrDomain::Edge, false); const VArray select_poly = *attributes.lookup_or_default( ".select_poly", AttrDomain::Face, false); for (i_src = 0, i_dst = 0; i_src < mesh->totselect; i_src++) { int index = mselect_src[i_src].index; switch (mselect_src[i_src].type) { case ME_VSEL: { if (select_vert[index]) { mselect_dst[i_dst] = mselect_src[i_src]; i_dst++; } break; } case ME_ESEL: { if (select_edge[index]) { mselect_dst[i_dst] = mselect_src[i_src]; i_dst++; } break; } case ME_FSEL: { if (select_poly[index]) { mselect_dst[i_dst] = mselect_src[i_src]; i_dst++; } break; } default: { BLI_assert_unreachable(); break; } } } MEM_delete(mselect_src); if (i_dst == 0) { MEM_delete(mselect_dst); mselect_dst = nullptr; } else if (i_dst != mesh->totselect) { mselect_dst = static_cast( MEM_realloc_uninitialized(mselect_dst, sizeof(MSelect) * i_dst)); } mesh->totselect = i_dst; mesh->mselect = mselect_dst; } int BKE_mesh_mselect_find(const Mesh *mesh, int index, eMSelect_Type type) { BLI_assert(ELEM(type, ME_VSEL, ME_ESEL, ME_FSEL)); for (int i = 0; i < mesh->totselect; i++) { if ((mesh->mselect[i].index == index) && (mesh->mselect[i].type == type)) { return i; } } return -1; } int BKE_mesh_mselect_active_get(const Mesh *mesh, eMSelect_Type type) { BLI_assert(ELEM(type, ME_VSEL, ME_ESEL, ME_FSEL)); if (mesh->totselect) { if (mesh->mselect[mesh->totselect - 1].type == type) { return mesh->mselect[mesh->totselect - 1].index; } } return -1; } void BKE_mesh_mselect_active_set(Mesh *mesh, int index, eMSelect_Type type) { const int msel_index = BKE_mesh_mselect_find(mesh, index, type); if (msel_index == -1) { /* add to the end */ mesh->mselect = static_cast( MEM_realloc_uninitialized(mesh->mselect, sizeof(MSelect) * (mesh->totselect + 1))); mesh->mselect[mesh->totselect].index = index; mesh->mselect[mesh->totselect].type = eMSelect_Type(type); mesh->totselect++; } else if (msel_index != mesh->totselect - 1) { /* move to the end */ std::swap(mesh->mselect[msel_index], mesh->mselect[mesh->totselect - 1]); } BLI_assert((mesh->mselect[mesh->totselect - 1].index == index) && (mesh->mselect[mesh->totselect - 1].type == type)); } void BKE_mesh_count_selected_items(const Mesh *mesh, int r_count[3]) { r_count[0] = r_count[1] = r_count[2] = 0; if (mesh->runtime->edit_mesh) { BMesh *bm = mesh->runtime->edit_mesh->bm; r_count[0] = bm->totvertsel; r_count[1] = bm->totedgesel; r_count[2] = bm->totfacesel; } /* We could support faces in paint modes. */ } /* **** Depsgraph evaluation **** */ void BKE_mesh_eval_geometry(Depsgraph *depsgraph, Mesh *mesh) { DEG_debug_print_eval(depsgraph, __func__, mesh->id.name, mesh); BKE_mesh_texspace_calc(mesh); /* We are here because something did change in the mesh. This means we can not trust the existing * evaluated mesh, and we don't know what parts of the mesh did change. So we simply delete the * evaluated mesh and let objects to re-create it with updated settings. */ if (mesh->runtime->mesh_eval != nullptr) { BKE_id_free(nullptr, mesh->runtime->mesh_eval); mesh->runtime->mesh_eval = nullptr; } if (DEG_is_active(depsgraph)) { Mesh *mesh_orig = DEG_get_original(mesh); if (mesh->texspace_flag & ME_TEXSPACE_FLAG_AUTO_EVALUATED) { mesh_orig->texspace_flag |= ME_TEXSPACE_FLAG_AUTO_EVALUATED; copy_v3_v3(mesh_orig->texspace_location, mesh->texspace_location); copy_v3_v3(mesh_orig->texspace_size, mesh->texspace_size); } } } } // namespace blender