Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/mesh_convert.cc
| Show First 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
| /* -- */ | /* -- */ | ||||
| #include "BKE_pointcloud.h" | #include "BKE_pointcloud.h" | ||||
| #include "BKE_curve_to_mesh.hh" | #include "BKE_curve_to_mesh.hh" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "DEG_depsgraph_query.h" | #include "DEG_depsgraph_query.h" | ||||
| using blender::float3; | |||||
| using blender::IndexRange; | using blender::IndexRange; | ||||
| using blender::MutableSpan; | using blender::MutableSpan; | ||||
| using blender::Span; | using blender::Span; | ||||
| using blender::StringRefNull; | |||||
| /* Define for cases when you want extra validation of mesh | /* Define for cases when you want extra validation of mesh | ||||
| * after certain modifications. | * after certain modifications. | ||||
| */ | */ | ||||
| // #undef VALIDATE_MESH | // #undef VALIDATE_MESH | ||||
| #ifdef VALIDATE_MESH | #ifdef VALIDATE_MESH | ||||
| # define ASSERT_IS_VALID_MESH(mesh) \ | # define ASSERT_IS_VALID_MESH(mesh) \ | ||||
| ▲ Show 20 Lines • Show All 1,005 Lines • ▼ Show 20 Lines | Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, | ||||
| /* NOTE: BKE_mesh_nomain_to_mesh() does not copy materials and instead it preserves them in the | /* NOTE: BKE_mesh_nomain_to_mesh() does not copy materials and instead it preserves them in the | ||||
| * destination mesh. So we "steal" all related fields before calling it. | * destination mesh. So we "steal" all related fields before calling it. | ||||
| * | * | ||||
| * TODO(sergey): We really better have a function which gets and ID and accepts it for the bmain. | * TODO(sergey): We really better have a function which gets and ID and accepts it for the bmain. | ||||
| */ | */ | ||||
| mesh_in_bmain->mat = mesh->mat; | mesh_in_bmain->mat = mesh->mat; | ||||
| mesh_in_bmain->totcol = mesh->totcol; | mesh_in_bmain->totcol = mesh->totcol; | ||||
| mesh_in_bmain->flag = mesh->flag; | |||||
| mesh_in_bmain->smoothresh = mesh->smoothresh; | |||||
| mesh->mat = nullptr; | mesh->mat = nullptr; | ||||
| mesh->totcol = 0; | |||||
| BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true); | BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr); | ||||
| /* Anonymous attributes shouldn't exist on original data. */ | /* Anonymous attributes shouldn't exist on original data. */ | ||||
| blender::bke::mesh_attributes_for_write(*mesh_in_bmain).remove_anonymous(); | blender::bke::mesh_attributes_for_write(*mesh_in_bmain).remove_anonymous(); | ||||
| /* User-count is required because so far mesh was in a limbo, where library management does | /* User-count is required because so far mesh was in a limbo, where library management does | ||||
| * not perform any user management (i.e. copy of a mesh will not increase users of materials). */ | * not perform any user management (i.e. copy of a mesh will not increase users of materials). */ | ||||
| BKE_library_foreach_ID_link( | BKE_library_foreach_ID_link( | ||||
| nullptr, &mesh_in_bmain->id, foreach_libblock_make_usercounts_callback, nullptr, IDWALK_NOP); | nullptr, &mesh_in_bmain->id, foreach_libblock_make_usercounts_callback, nullptr, IDWALK_NOP); | ||||
| ▲ Show 20 Lines • Show All 137 Lines • ▼ Show 20 Lines | Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, | ||||
| if (deformedVerts != nullptr) { | if (deformedVerts != nullptr) { | ||||
| MEM_freeN(deformedVerts); | MEM_freeN(deformedVerts); | ||||
| } | } | ||||
| return result; | return result; | ||||
| } | } | ||||
| /* This is a Mesh-based copy of the same function in DerivedMesh.cc */ | static KeyBlock *keyblock_ensure_from_uid(Key &key, const int uid, const StringRefNull name) | ||||
| static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int actshape_uid) | |||||
| { | { | ||||
| KeyBlock *kb; | if (KeyBlock *kb = BKE_keyblock_find_uid(&key, uid)) { | ||||
| int i, j, tot; | return kb; | ||||
| if (!mesh_dst->key) { | |||||
| return; | |||||
| } | |||||
| tot = CustomData_number_of_layers(&mesh_src->vdata, CD_SHAPEKEY); | |||||
| for (i = 0; i < tot; i++) { | |||||
| CustomDataLayer *layer = | |||||
| &mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)]; | |||||
| float(*kbcos)[3]; | |||||
| for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) { | |||||
| if (kb->uid == layer->uid) { | |||||
| break; | |||||
| } | } | ||||
| KeyBlock *kb = BKE_keyblock_add(&key, name.c_str()); | |||||
| kb->uid = uid; | |||||
| return kb; | |||||
| } | } | ||||
| static int find_object_active_key_uid(const Key &key, const Object &object) | |||||
| { | |||||
| const int active_kb_index = object.shapenr - 1; | |||||
| const KeyBlock *kb = (const KeyBlock *)BLI_findlink(&key.block, active_kb_index); | |||||
| if (!kb) { | if (!kb) { | ||||
| kb = BKE_keyblock_add(mesh_dst->key, layer->name); | CLOG_ERROR(&LOG, "Could not find object's active shapekey %d", active_kb_index); | ||||
| kb->uid = layer->uid; | return -1; | ||||
| } | } | ||||
| return kb->uid; | |||||
| if (kb->data) { | |||||
| MEM_freeN(kb->data); | |||||
| } | } | ||||
| const float(*cos)[3] = (const float(*)[3])CustomData_get_layer_n( | static void move_shapekey_layers_to_keyblocks(Mesh &mesh, Key &key_dst, const int actshape_uid) | ||||
| &mesh_src->vdata, CD_SHAPEKEY, i); | { | ||||
| kb->totelem = mesh_src->totvert; | using namespace blender::bke; | ||||
| for (const int i : IndexRange(CustomData_number_of_layers(&mesh.vdata, CD_SHAPEKEY))) { | |||||
| const int layer_index = CustomData_get_layer_index_n(&mesh.vdata, CD_SHAPEKEY, i); | |||||
| CustomDataLayer &layer = mesh.vdata.layers[layer_index]; | |||||
| KeyBlock *kb = keyblock_ensure_from_uid(key_dst, layer.uid, layer.name); | |||||
| MEM_SAFE_FREE(kb->data); | |||||
| kb->totelem = mesh.totvert; | |||||
| kb->data = kbcos = (float(*)[3])MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__); | |||||
| if (kb->uid == actshape_uid) { | if (kb->uid == actshape_uid) { | ||||
| const Span<MVert> verts = mesh_src->vertices(); | const Span<float3> layer_coords(static_cast<const float3 *>(layer.data), mesh.totvert); | ||||
| for (j = 0; j < mesh_src->totvert; j++, kbcos++) { | kb->data = MEM_malloc_arrayN(kb->totelem, sizeof(float3), __func__); | ||||
| copy_v3_v3(*kbcos, verts[j].co); | MutableSpan<float3> kb_coords(static_cast<float3 *>(kb->data), kb->totelem); | ||||
| } | mesh_attributes(mesh).lookup<float3>("position").materialize(kb_coords); | ||||
campbellbarton: This isn't used, for some reason it doesn't show as a compiler warning, assume it can be… | |||||
| } | } | ||||
| else { | else { | ||||
| for (j = 0; j < kb->totelem; j++, cos++, kbcos++) { | kb->data = layer.data; | ||||
| copy_v3_v3(*kbcos, *cos); | layer.data = nullptr; | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) { | |||||
| if (kb->totelem != mesh_src->totvert) { | |||||
| if (kb->data) { | |||||
| MEM_freeN(kb->data); | |||||
| } | |||||
| kb->totelem = mesh_src->totvert; | LISTBASE_FOREACH (KeyBlock *, kb, &key_dst.block) { | ||||
| kb->data = MEM_calloc_arrayN(kb->totelem, sizeof(float[3]), __func__); | if (kb->totelem != mesh.totvert) { | ||||
| CLOG_ERROR(&LOG, "lost a shapekey layer: '%s'! (bmesh internal error)", kb->name); | MEM_SAFE_FREE(kb->data); | ||||
| } | } | ||||
| kb->totelem = mesh.totvert; | |||||
| kb->data = MEM_cnew_array<float3>(kb->totelem, __func__); | |||||
| CLOG_ERROR(&LOG, "Data for shape key '%s' on mesh missing from evaluated mesh ", kb->name); | |||||
| } | } | ||||
| } | } | ||||
| void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, | void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob) | ||||
| Mesh *mesh_dst, | |||||
| Object *ob, | |||||
| const CustomData_MeshMasks *mask, | |||||
| bool take_ownership) | |||||
| { | { | ||||
| using namespace blender::bke; | using namespace blender::bke; | ||||
| BLI_assert(mesh_src->id.tag & LIB_TAG_NO_MAIN); | BLI_assert(mesh_src->id.tag & LIB_TAG_NO_MAIN); | ||||
| /* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */ | |||||
| /* TODO(Sybren): the above claim came from 2.7x derived-mesh code (DM_to_mesh); | |||||
| * check whether it is still true with Mesh */ | |||||
| Mesh tmp = blender::dna::shallow_copy(*mesh_dst); | |||||
| int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly; | |||||
| bool did_shapekeys = false; | |||||
| eCDAllocType alloctype = CD_DUPLICATE; | |||||
| if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) { | |||||
| bool has_any_referenced_layers = CustomData_has_referenced(&mesh_src->vdata) || | |||||
| CustomData_has_referenced(&mesh_src->edata) || | |||||
| CustomData_has_referenced(&mesh_src->ldata) || | |||||
| CustomData_has_referenced(&mesh_src->fdata) || | |||||
| CustomData_has_referenced(&mesh_src->pdata); | |||||
| if (!has_any_referenced_layers) { | |||||
| alloctype = CD_ASSIGN; | |||||
| } | |||||
| } | |||||
| CustomData_reset(&tmp.vdata); | |||||
| CustomData_reset(&tmp.edata); | |||||
| CustomData_reset(&tmp.fdata); | |||||
| CustomData_reset(&tmp.ldata); | |||||
| CustomData_reset(&tmp.pdata); | |||||
| totvert = tmp.totvert = mesh_src->totvert; | |||||
| totedge = tmp.totedge = mesh_src->totedge; | |||||
| totloop = tmp.totloop = mesh_src->totloop; | |||||
| totpoly = tmp.totpoly = mesh_src->totpoly; | |||||
| tmp.totface = 0; | |||||
| CustomData_copy(&mesh_src->vdata, &tmp.vdata, mask->vmask, alloctype, totvert); | |||||
| CustomData_copy(&mesh_src->edata, &tmp.edata, mask->emask, alloctype, totedge); | |||||
| CustomData_copy(&mesh_src->ldata, &tmp.ldata, mask->lmask, alloctype, totloop); | |||||
| CustomData_copy(&mesh_src->pdata, &tmp.pdata, mask->pmask, alloctype, totpoly); | |||||
| tmp.cd_flag = mesh_src->cd_flag; | |||||
| tmp.runtime.deformed_only = mesh_src->runtime.deformed_only; | |||||
| /* Clear the normals completely, since the new vertex / polygon count might be different. */ | |||||
| BKE_mesh_clear_derived_normals(&tmp); | |||||
| if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) { | |||||
| KeyBlock *kb; | |||||
| int uid; | |||||
| if (ob) { | |||||
| kb = (KeyBlock *)BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1); | |||||
| if (kb) { | |||||
| uid = kb->uid; | |||||
| } | |||||
| else { | |||||
| CLOG_ERROR(&LOG, "could not find active shapekey %d!", ob->shapenr - 1); | |||||
| uid = INT_MAX; | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* if no object, set to INT_MAX so we don't mess up any shapekey layers */ | |||||
| uid = INT_MAX; | |||||
| } | |||||
| shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid); | |||||
| did_shapekeys = true; | |||||
| } | |||||
| /* copy texture space */ | |||||
| if (ob) { | if (ob) { | ||||
| BKE_mesh_texspace_copy_from_object(&tmp, ob); | BLI_assert(mesh_dst == ob->data); | ||||
| } | } | ||||
| /* not all DerivedMeshes store their verts/edges/faces in CustomData, so | BKE_mesh_clear_geometry(mesh_dst); | ||||
| * we set them here in case they are missing */ | |||||
| /* TODO(Sybren): we could probably replace CD_ASSIGN with alloctype and | |||||
| * always directly pass mesh_src->mxxx, instead of using a ternary operator. */ | |||||
| if (!CustomData_has_layer(&tmp.vdata, CD_MVERT)) { | |||||
| CustomData_add_layer(&tmp.vdata, | |||||
| CD_MVERT, | |||||
| CD_ASSIGN, | |||||
| (alloctype == CD_ASSIGN) ? mesh_src->vertices_for_write().data() : | |||||
| MEM_dupallocN(mesh_src->vertices().data()), | |||||
| totvert); | |||||
| } | |||||
| if (!CustomData_has_layer(&tmp.edata, CD_MEDGE)) { | |||||
| CustomData_add_layer(&tmp.edata, | |||||
| CD_MEDGE, | |||||
| CD_ASSIGN, | |||||
| (alloctype == CD_ASSIGN) ? mesh_src->edges_for_write().data() : | |||||
| MEM_dupallocN(mesh_src->edges().data()), | |||||
| totedge); | |||||
| } | |||||
| if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) { | |||||
| CustomData_add_layer(&tmp.ldata, | |||||
| CD_MLOOP, | |||||
| CD_ASSIGN, | |||||
| (alloctype == CD_ASSIGN) ? mesh_src->loops_for_write().data() : | |||||
| MEM_dupallocN(mesh_src->loops().data()), | |||||
| tmp.totloop); | |||||
| CustomData_add_layer(&tmp.pdata, | |||||
| CD_MPOLY, | |||||
| CD_ASSIGN, | |||||
| (alloctype == CD_ASSIGN) ? mesh_src->polygons_for_write().data() : | |||||
| MEM_dupallocN(mesh_src->polygons().data()), | |||||
| tmp.totpoly); | |||||
| } | |||||
| /* object had got displacement layer, should copy this layer to save sculpted data */ | |||||
| /* NOTE(nazgul): maybe some other layers should be copied? */ | |||||
| if (CustomData_has_layer(&mesh_dst->ldata, CD_MDISPS)) { | |||||
| if (totloop == mesh_dst->totloop) { | |||||
| MDisps *mdisps = (MDisps *)CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS); | |||||
| CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop); | |||||
| if (alloctype == CD_ASSIGN) { | |||||
| /* Assign nullptr to prevent double-free. */ | |||||
| CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, nullptr); | |||||
| } | |||||
| } | |||||
| } | |||||
| CustomData_free(&mesh_dst->vdata, mesh_dst->totvert); | |||||
| CustomData_free(&mesh_dst->edata, mesh_dst->totedge); | |||||
| CustomData_free(&mesh_dst->fdata, mesh_dst->totface); | |||||
| CustomData_free(&mesh_dst->ldata, mesh_dst->totloop); | |||||
| CustomData_free(&mesh_dst->pdata, mesh_dst->totpoly); | |||||
| /* ok, this should now use new CD shapekey data, | |||||
| * which should be fed through the modifier | |||||
| * stack */ | |||||
| if (tmp.totvert != mesh_dst->totvert && !did_shapekeys && mesh_dst->key) { | |||||
| CLOG_ERROR(&LOG, "YEEK! this should be recoded! Shape key loss!: ID '%s'", tmp.id.name); | |||||
| if (tmp.key && !(tmp.id.tag & LIB_TAG_NO_MAIN)) { | |||||
| id_us_min(&tmp.key->id); | |||||
| } | |||||
| tmp.key = nullptr; | |||||
| } | |||||
| /* Clear selection history */ | |||||
| MEM_SAFE_FREE(tmp.mselect); | |||||
| tmp.totselect = 0; | |||||
| tmp.texflag &= ~ME_AUTOSPACE_EVALUATED; | |||||
| /* Clear any run-time data. | |||||
| * Even though this mesh won't typically have run-time data, the Python API can for e.g. | |||||
| * create loop-triangle cache here, which is confusing when left in the mesh, see: T81136. */ | |||||
| BKE_mesh_runtime_clear_geometry(&tmp); | |||||
| /* skip the listbase */ | /* Make sure referenced layers have a single user so assigning them to the mesh in main doesn't | ||||
| MEMCPY_STRUCT_AFTER(mesh_dst, &tmp, id.prev); | * share them. "Referenced" layers are not expected to be shared between original meshes. */ | ||||
| CustomData_duplicate_referenced_layers(&mesh_src->vdata, mesh_src->totvert); | |||||
| CustomData_duplicate_referenced_layers(&mesh_src->edata, mesh_src->totedge); | |||||
| CustomData_duplicate_referenced_layers(&mesh_src->pdata, mesh_src->totpoly); | |||||
| CustomData_duplicate_referenced_layers(&mesh_src->ldata, mesh_src->totloop); | |||||
| mesh_dst->totvert = mesh_src->totvert; | |||||
| mesh_dst->totedge = mesh_src->totedge; | |||||
| mesh_dst->totpoly = mesh_src->totpoly; | |||||
| mesh_dst->totloop = mesh_src->totloop; | |||||
| /* Using #CD_MASK_MESH ensures that only data that should exist in Main meshes is moved. */ | |||||
| const CustomData_MeshMasks mask = CD_MASK_MESH; | |||||
| CustomData_copy(&mesh_src->vdata, &mesh_dst->vdata, mask.vmask, CD_ASSIGN, mesh_src->totvert); | |||||
| CustomData_copy(&mesh_src->edata, &mesh_dst->edata, mask.emask, CD_ASSIGN, mesh_src->totedge); | |||||
| CustomData_copy(&mesh_src->pdata, &mesh_dst->pdata, mask.pmask, CD_ASSIGN, mesh_src->totpoly); | |||||
| CustomData_copy(&mesh_src->ldata, &mesh_dst->ldata, mask.lmask, CD_ASSIGN, mesh_src->totloop); | |||||
| BLI_freelistN(&mesh_dst->vertex_group_names); | BLI_freelistN(&mesh_dst->vertex_group_names); | ||||
| BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names); | mesh_dst->vertex_group_names = mesh_src->vertex_group_names; | ||||
| mesh_dst->vertex_group_active_index = mesh_src->vertex_group_active_index; | BLI_listbase_clear(&mesh_src->vertex_group_names); | ||||
Not Done Inline Actionstypo (evaluation) JacquesLucke: typo (`evaluation`) | |||||
| if (take_ownership) { | BKE_mesh_copy_parameters(mesh_dst, mesh_src); | ||||
| if (alloctype == CD_ASSIGN) { | mesh_dst->cd_flag = mesh_src->cd_flag; | ||||
| CustomData_free_typemask(&mesh_src->vdata, mesh_src->totvert, ~mask->vmask); | |||||
| CustomData_free_typemask(&mesh_src->edata, mesh_src->totedge, ~mask->emask); | /* For original meshes, shape key data is stored in the #Key data-block, so it | ||||
| CustomData_free_typemask(&mesh_src->ldata, mesh_src->totloop, ~mask->lmask); | * must be moved from the storage in #CustomData layers used for evaluation. */ | ||||
| CustomData_free_typemask(&mesh_src->pdata, mesh_src->totpoly, ~mask->pmask); | if (Key *key_dst = mesh_dst->key) { | ||||
| if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) { | |||||
| /* If no object, set to -1 so we don't mess up any shapekey layers. */ | |||||
| const int uid_active = ob ? find_object_active_key_uid(*key_dst, *ob) : -1; | |||||
| move_shapekey_layers_to_keyblocks(*mesh_src, *key_dst, uid_active); | |||||
| } | |||||
| else if (mesh_src->totvert != mesh_dst->totvert) { | |||||
| CLOG_ERROR(&LOG, "Mesh in Main '%s' lost shape keys", mesh_src->id.name); | |||||
| if (mesh_src->key) { | |||||
| id_us_min(&mesh_src->key->id); | |||||
| } | |||||
| } | } | ||||
| BKE_id_free(nullptr, mesh_src); | |||||
| } | } | ||||
| BKE_mesh_assert_normals_dirty_or_calculated(mesh_dst); | BKE_id_free(nullptr, mesh_src); | ||||
| } | } | ||||
| void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb) | void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb) | ||||
| { | { | ||||
| BLI_assert(mesh_src->id.tag & LIB_TAG_NO_MAIN); | BLI_assert(mesh_src->id.tag & LIB_TAG_NO_MAIN); | ||||
| int a, totvert = mesh_src->totvert; | int a, totvert = mesh_src->totvert; | ||||
| float *fp; | float *fp; | ||||
| Show All 17 Lines | |||||
This isn't used, for some reason it doesn't show as a compiler warning, assume it can be removed.