Files
test2/source/blender/blenkernel/intern/mesh.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1446 lines
46 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
2002-10-12 11:37:38 +00:00
/** \file
* \ingroup bke
2011-02-27 20:40:57 +00:00
*/
2002-10-12 11:37:38 +00:00
#include "MEM_guardedalloc.h"
/* Allow using deprecated functionality for .blend file I/O. */
#define DNA_DEPRECATED_ALLOW
#include "DNA_defaults.h"
2002-10-12 11:37:38 +00:00
#include "DNA_key_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
2018-06-26 17:45:00 +02:00
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
2002-10-12 11:37:38 +00:00
Mesh: Move positions to a generic attribute **Changes** As described in T93602, this patch removes all use of the `MVert` struct, replacing it with a generic named attribute with the name `"position"`, consistent with other geometry types. Variable names have been changed from `verts` to `positions`, to align with the attribute name and the more generic design (positions are not vertices, they are just an attribute stored on the point domain). This change is made possible by previous commits that moved all other data out of `MVert` to runtime data or other generic attributes. What remains is mostly a simple type change. Though, the type still shows up 859 times, so the patch is quite large. One compromise is that now `CD_MASK_BAREMESH` now contains `CD_PROP_FLOAT3`. With the general move towards generic attributes over custom data types, we are removing use of these type masks anyway. **Benefits** The most obvious benefit is reduced memory usage and the benefits that brings in memory-bound situations. `float3` is only 3 bytes, in comparison to `MVert` which was 4. When there are millions of vertices this starts to matter more. The other benefits come from using a more generic type. Instead of writing algorithms specifically for `MVert`, code can just use arrays of vectors. This will allow eliminating many temporary arrays or wrappers used to extract positions. Many possible improvements aren't implemented in this patch, though I did switch simplify or remove the process of creating temporary position arrays in a few places. The design clarity that "positions are just another attribute" brings allows removing explicit copying of vertices in some procedural operations-- they are just processed like most other attributes. **Performance** This touches so many areas that it's hard to benchmark exhaustively, but I observed some areas as examples. * The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster. * The Spring splash screen went from ~4.3 to ~4.5 fps. * The subdivision surface modifier/node was slightly faster RNA access through Python may be slightly slower, since now we need a name lookup instead of just a custom data type lookup for each index. **Future Improvements** * Remove uses of "vert_coords" functions: * `BKE_mesh_vert_coords_alloc` * `BKE_mesh_vert_coords_get` * `BKE_mesh_vert_coords_apply{_with_mat4}` * Remove more hidden copying of positions * General simplification now possible in many areas * Convert more code to C++ to use `float3` instead of `float[3]` * Currently `reinterpret_cast` is used for those C-API functions Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
#include "BLI_bounds.hh"
#include "BLI_endian_switch.h"
2020-03-05 14:53:23 +01:00
#include "BLI_ghash.h"
#include "BLI_hash.h"
#include "BLI_implicit_sharing.hh"
#include "BLI_index_range.hh"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.hh"
#include "BLI_memarena.h"
#include "BLI_ordered_edge.hh"
Mesh: Move positions to a generic attribute **Changes** As described in T93602, this patch removes all use of the `MVert` struct, replacing it with a generic named attribute with the name `"position"`, consistent with other geometry types. Variable names have been changed from `verts` to `positions`, to align with the attribute name and the more generic design (positions are not vertices, they are just an attribute stored on the point domain). This change is made possible by previous commits that moved all other data out of `MVert` to runtime data or other generic attributes. What remains is mostly a simple type change. Though, the type still shows up 859 times, so the patch is quite large. One compromise is that now `CD_MASK_BAREMESH` now contains `CD_PROP_FLOAT3`. With the general move towards generic attributes over custom data types, we are removing use of these type masks anyway. **Benefits** The most obvious benefit is reduced memory usage and the benefits that brings in memory-bound situations. `float3` is only 3 bytes, in comparison to `MVert` which was 4. When there are millions of vertices this starts to matter more. The other benefits come from using a more generic type. Instead of writing algorithms specifically for `MVert`, code can just use arrays of vectors. This will allow eliminating many temporary arrays or wrappers used to extract positions. Many possible improvements aren't implemented in this patch, though I did switch simplify or remove the process of creating temporary position arrays in a few places. The design clarity that "positions are just another attribute" brings allows removing explicit copying of vertices in some procedural operations-- they are just processed like most other attributes. **Performance** This touches so many areas that it's hard to benchmark exhaustively, but I observed some areas as examples. * The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster. * The Spring splash screen went from ~4.3 to ~4.5 fps. * The subdivision surface modifier/node was slightly faster RNA access through Python may be slightly slower, since now we need a name lookup instead of just a custom data type lookup for each index. **Future Improvements** * Remove uses of "vert_coords" functions: * `BKE_mesh_vert_coords_alloc` * `BKE_mesh_vert_coords_get` * `BKE_mesh_vert_coords_apply{_with_mat4}` * Remove more hidden copying of positions * General simplification now possible in many areas * Convert more code to C++ to use `float3` instead of `float[3]` * Currently `reinterpret_cast` is used for those C-API functions Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
#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.h"
#include "BKE_anim_data.h"
#include "BKE_attribute.hh"
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
#include "BKE_bake_data_block_id.hh"
#include "BKE_bpath.h"
2024-01-29 18:57:16 -05:00
#include "BKE_deform.hh"
#include "BKE_editmesh.hh"
#include "BKE_editmesh_cache.hh"
2002-10-12 11:37:38 +00:00
#include "BKE_global.h"
#include "BKE_idtype.hh"
2024-01-30 14:42:07 -05:00
#include "BKE_key.hh"
2024-01-15 12:44:04 -05:00
#include "BKE_lib_id.hh"
#include "BKE_lib_query.hh"
#include "BKE_main.hh"
2002-10-12 11:37:38 +00:00
#include "BKE_material.h"
#include "BKE_mesh.hh"
#include "BKE_mesh_legacy_convert.hh"
#include "BKE_mesh_runtime.hh"
#include "BKE_mesh_wrapper.hh"
2023-11-14 09:30:40 +01:00
#include "BKE_modifier.hh"
#include "BKE_multires.hh"
#include "BKE_object.hh"
2002-10-12 11:37:38 +00:00
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "BLO_read_write.hh"
using blender::float3;
using blender::int2;
using blender::MutableSpan;
using blender::OffsetIndices;
Mesh: Remove redundant custom data pointers For copy-on-write, we want to share attribute arrays between meshes where possible. Mutable pointers like `Mesh.mvert` make that difficult by making ownership vague. They also make code more complex by adding redundancy. The simplest solution is just removing them and retrieving layers from `CustomData` as needed. Similar changes have already been applied to curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of the pointers generally makes code more obvious and more reusable. Mesh data is now accessed with a C++ API (`Mesh::edges()` or `Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`). The CoW changes this commit makes possible are described in T95845 and T95842, and started in D14139 and D14140. The change also simplifies the ongoing mesh struct-of-array refactors from T95965. **RNA/Python Access Performance** Theoretically, accessing mesh elements with the RNA API may become slower, since the layer needs to be found on every random access. However, overhead is already high enough that this doesn't make a noticible differenc, and performance is actually improved in some cases. Random access can be up to 10% faster, but other situations might be a bit slower. Generally using `foreach_get/set` are the best way to improve performance. See the differential revision for more discussion about Python performance. Cycles has been updated to use raw pointers and the internal Blender mesh types, mostly because there is no sense in having this overhead when it's already compiled with Blender. In my tests this roughly halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million face grid). Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
using blender::Span;
2022-09-23 09:38:37 -05:00
using blender::StringRef;
using blender::VArray;
using blender::Vector;
static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata);
static void mesh_init_data(ID *id)
{
Mesh *mesh = reinterpret_cast<Mesh *>(id);
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mesh, id));
MEMCPY_STRUCT_AFTER(mesh, DNA_struct_default_get(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);
mesh->runtime = new blender::bke::MeshRuntime();
mesh->face_sets_color_seed = BLI_hash_int(BLI_check_seconds_timer_i() & UINT_MAX);
}
static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int flag)
{
Mesh *mesh_dst = reinterpret_cast<Mesh *>(id_dst);
const Mesh *mesh_src = reinterpret_cast<const Mesh *>(id_src);
mesh_dst->runtime = new blender::bke::MeshRuntime();
mesh_dst->runtime->deformed_only = mesh_src->runtime->deformed_only;
mesh_dst->runtime->wrapper_type = mesh_src->runtime->wrapper_type;
mesh_dst->runtime->wrapper_type_finalize = mesh_src->runtime->wrapper_type_finalize;
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 & LIB_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. */
Geometry: Cache bounds min and max, share between data-blocks Bounding box calculation can be a large in some situations, especially instancing. This patch caches the min and max of the bounding box in runtime data of meshes, point clouds, and curves, implementing part of T96968. Bounds are now calculated lazily-- only after they are tagged dirty. Also, cached bounds are also shared when copying geometry data-blocks that have equivalent data. When bounds are calculated on an evaluated data-block, they are also accessible on the original, and the next evaluated ID will also share them. A geometry will stop sharing bounds as soon as its positions (or radii) are changed. Just caching the bounds gave a 2-3x speedup with thousands of mesh geometry instances in the viewport. Sharing the bounds can eliminate recalculations entirely in cases like copying meshes in geometry nodes or the selection paint brush in curves sculpt mode, which causes a reevaluation but doesn't change the positions. **Implementation** The sharing is achieved with a `shared_ptr` that points to a cache mutex (from D16419) and the cached bounds data. When geometries are copied, the bounds are shared by default, and only "un-shared" when the bounds are tagged dirty. Point clouds have a new runtime struct to store this data. Functions for tagging the data dirty are improved for added for point clouds and improved for curves. A missing tag has also been fixed for mesh sculpt mode. **Future** There are further improvements which can be worked on next - Apply changes to volume objects and other types where it makes sense - Continue cleanup changes described in T96968 - Apply shared cache design to more expensive data like triangulation or normals Differential Revision: https://developer.blender.org/D16204
2022-11-15 13:46:55 -06:00
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->face_normals_cache = mesh_src->runtime->face_normals_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;
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
if (mesh_src->runtime->bake_materials) {
mesh_dst->runtime->bake_materials = std::make_unique<blender::bke::bake::BakeMaterialsList>(
*mesh_src->runtime->bake_materials);
}
Geometry: Cache bounds min and max, share between data-blocks Bounding box calculation can be a large in some situations, especially instancing. This patch caches the min and max of the bounding box in runtime data of meshes, point clouds, and curves, implementing part of T96968. Bounds are now calculated lazily-- only after they are tagged dirty. Also, cached bounds are also shared when copying geometry data-blocks that have equivalent data. When bounds are calculated on an evaluated data-block, they are also accessible on the original, and the next evaluated ID will also share them. A geometry will stop sharing bounds as soon as its positions (or radii) are changed. Just caching the bounds gave a 2-3x speedup with thousands of mesh geometry instances in the viewport. Sharing the bounds can eliminate recalculations entirely in cases like copying meshes in geometry nodes or the selection paint brush in curves sculpt mode, which causes a reevaluation but doesn't change the positions. **Implementation** The sharing is achieved with a `shared_ptr` that points to a cache mutex (from D16419) and the cached bounds data. When geometries are copied, the bounds are shared by default, and only "un-shared" when the bounds are tagged dirty. Point clouds have a new runtime struct to store this data. Functions for tagging the data dirty are improved for added for point clouds and improved for curves. A missing tag has also been fixed for mesh sculpt mode. **Future** There are further improvements which can be worked on next - Apply changes to volume objects and other types where it makes sense - Continue cleanup changes described in T96968 - Apply shared cache design to more expensive data like triangulation or normals Differential Revision: https://developer.blender.org/D16204
2022-11-15 13:46:55 -06:00
/* 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 & LIB_TAG_NO_MAIN) {
/* For copies in depsgraph, keep data like #CD_ORIGINDEX and #CD_ORCO. */
CustomData_MeshMasks_update(&mask, &CD_MASK_DERIVEDMESH);
}
mesh_dst->mat = (Material **)MEM_dupallocN(mesh_src->mat);
BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names);
mesh_dst->active_color_attribute = static_cast<char *>(
MEM_dupallocN(mesh_src->active_color_attribute));
mesh_dst->default_color_attribute = static_cast<char *>(
MEM_dupallocN(mesh_src->default_color_attribute));
CustomData_copy(&mesh_src->vert_data, &mesh_dst->vert_data, mask.vmask, mesh_dst->verts_num);
CustomData_copy(&mesh_src->edge_data, &mesh_dst->edge_data, mask.emask, mesh_dst->edges_num);
CustomData_copy(
&mesh_src->corner_data, &mesh_dst->corner_data, mask.lmask, mesh_dst->corners_num);
CustomData_copy(&mesh_src->face_data, &mesh_dst->face_data, mask.pmask, mesh_dst->faces_num);
blender::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) {
2023-07-24 16:32:08 -04:00
CustomData_copy(
&mesh_src->fdata_legacy, &mesh_dst->fdata_legacy, mask.fmask, mesh_dst->totface_legacy);
}
else {
mesh_tessface_clear_intern(mesh_dst, false);
}
mesh_dst->edit_mesh = nullptr;
mesh_dst->mselect = (MSelect *)MEM_dupallocN(mesh_dst->mselect);
/* TODO: Do we want to add flag to prevent this? */
if (mesh_src->key && (flag & LIB_ID_COPY_SHAPEKEY)) {
BKE_id_copy_ex(bmain, &mesh_src->key->id, (ID **)&mesh_dst->key, flag);
/* XXX This is not nice, we need to make BKE_id_copy_ex fully re-entrant... */
mesh_dst->key->from = &mesh_dst->id;
}
}
void BKE_mesh_free_editmesh(Mesh *mesh)
Fix depsgraphs sharing IDs via evaluated edit mesh The evaluated mesh is a result of evaluated modifiers, and referencing other evaluated IDs such as materials. It can not be stored in the EditMesh structure which is intended to be re-used by many areas. Such sharing was causing ownership errors causing bugs like T93855: Cycles crash with edit mode and simultaneous viewport and final render The proposed solution is to store the evaluated edit mesh and its cage in the object's runtime field. The motivation goes as following: - It allows to avoid ownership problems like the ones in the linked report. - Object level is chosen over mesh level is because the evaluated mesh is affected by modifiers, which are on the object level. This patch allows to have modifier stack of an object which shares mesh with an object which is in edit mode to be properly taken into account (before the change the modifier stack from the active object will be used for all objects which share the mesh). There is a change in the way how copy-on-write is handled in the edit mode to allow proper state update when changing active scene (or having two windows with different scenes). Previously, the copt-on-write would have been ignored by skipping tagging CoW component. Now it is ignored from within the CoW operation callback. This allows to update edit pointers for objects which are not from the current depsgraph and where the edit_mesh was never assigned in the case when the depsgraph was evaluated prior the active depsgraph. There is no user level changes changes expected with the CoW handling changes: should not affect on neither performance, nor memory consumption. Tested scenarios: - Various modifiers configurations of objects sharing mesh and be part of the same scene. - Steps from the reports: T93855, T82952, T77359 This also fixes T76609, T72733 and perhaps other reports. Differential Revision: https://developer.blender.org/D13824
2022-01-11 15:42:07 +01:00
{
if (mesh->edit_mesh == nullptr) {
return;
}
if (mesh->edit_mesh->is_shallow_copy == false) {
BKE_editmesh_free_data(mesh->edit_mesh);
}
MEM_freeN(mesh->edit_mesh);
mesh->edit_mesh = nullptr;
}
static void mesh_free_data(ID *id)
{
Mesh *mesh = reinterpret_cast<Mesh *>(id);
Fix depsgraphs sharing IDs via evaluated edit mesh The evaluated mesh is a result of evaluated modifiers, and referencing other evaluated IDs such as materials. It can not be stored in the EditMesh structure which is intended to be re-used by many areas. Such sharing was causing ownership errors causing bugs like T93855: Cycles crash with edit mode and simultaneous viewport and final render The proposed solution is to store the evaluated edit mesh and its cage in the object's runtime field. The motivation goes as following: - It allows to avoid ownership problems like the ones in the linked report. - Object level is chosen over mesh level is because the evaluated mesh is affected by modifiers, which are on the object level. This patch allows to have modifier stack of an object which shares mesh with an object which is in edit mode to be properly taken into account (before the change the modifier stack from the active object will be used for all objects which share the mesh). There is a change in the way how copy-on-write is handled in the edit mode to allow proper state update when changing active scene (or having two windows with different scenes). Previously, the copt-on-write would have been ignored by skipping tagging CoW component. Now it is ignored from within the CoW operation callback. This allows to update edit pointers for objects which are not from the current depsgraph and where the edit_mesh was never assigned in the case when the depsgraph was evaluated prior the active depsgraph. There is no user level changes changes expected with the CoW handling changes: should not affect on neither performance, nor memory consumption. Tested scenarios: - Various modifiers configurations of objects sharing mesh and be part of the same scene. - Steps from the reports: T93855, T82952, T77359 This also fixes T76609, T72733 and perhaps other reports. Differential Revision: https://developer.blender.org/D13824
2022-01-11 15:42:07 +01:00
BKE_mesh_free_editmesh(mesh);
BKE_mesh_clear_geometry_and_metadata(mesh);
MEM_SAFE_FREE(mesh->mat);
delete mesh->runtime;
}
static void mesh_foreach_id(ID *id, LibraryForeachIDData *data)
{
Mesh *mesh = reinterpret_cast<Mesh *>(id);
const int flag = BKE_lib_query_foreachid_process_flags_get(data);
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);
}
if (flag & IDWALK_DO_DEPRECATED_POINTERS) {
BKE_LIB_FOREACHID_PROCESS_ID_NOCHECK(data, mesh->ipo, IDWALK_CB_USER);
}
}
static void mesh_foreach_path(ID *id, BPathForeachPathData *bpath_data)
{
Mesh *mesh = reinterpret_cast<Mesh *>(id);
if (mesh->corner_data.external) {
BKE_bpath_foreach_path_fixed_process(bpath_data,
mesh->corner_data.external->filepath,
sizeof(mesh->corner_data.external->filepath));
}
}
static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
using namespace blender;
using namespace blender::bke;
Mesh *mesh = reinterpret_cast<Mesh *>(id);
const bool is_undo = BLO_write_is_undo(writer);
Vector<CustomDataLayer, 16> vert_layers;
Vector<CustomDataLayer, 16> edge_layers;
Vector<CustomDataLayer, 16> loop_layers;
Vector<CustomDataLayer, 16> face_layers;
/* Cache only - don't write. */
mesh->mface = nullptr;
mesh->totface_legacy = 0;
memset(&mesh->fdata_legacy, 0, sizeof(mesh->fdata_legacy));
/* 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;
memset(&mesh->vert_data, 0, sizeof(mesh->vert_data));
mesh->edges_num = 0;
memset(&mesh->edge_data, 0, sizeof(mesh->edge_data));
mesh->corners_num = 0;
memset(&mesh->corner_data, 0, sizeof(mesh->corner_data));
mesh->faces_num = 0;
memset(&mesh->face_data, 0, sizeof(mesh->face_data));
mesh->face_offset_indices = nullptr;
}
else {
CustomData_blend_write_prepare(mesh->vert_data, vert_layers, {});
CustomData_blend_write_prepare(mesh->edge_data, edge_layers, {});
CustomData_blend_write_prepare(mesh->corner_data, loop_layers, {});
CustomData_blend_write_prepare(mesh->face_data, face_layers, {});
if (!is_undo) {
mesh_sculpt_mask_to_legacy(vert_layers);
}
}
mesh->runtime = nullptr;
BLO_write_id_struct(writer, Mesh, id_address, &mesh->id);
BKE_id_blend_write(writer, &mesh->id);
BKE_defbase_blend_write(writer, &mesh->vertex_group_names);
BLO_write_string(writer, mesh->active_color_attribute);
BLO_write_string(writer, mesh->default_color_attribute);
BLO_write_pointer_array(writer, mesh->totcol, mesh->mat);
BLO_write_raw(writer, sizeof(MSelect) * 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: Replace MPoly struct with offset indices Implements #95967. Currently the `MPoly` struct is 12 bytes, and stores the index of a face's first corner and the number of corners/verts/edges. Polygons and corners are always created in order by Blender, meaning each face's corners will be after the previous face's corners. We can take advantage of this fact and eliminate the redundancy in mesh face storage by only storing a single integer corner offset for each face. The size of the face is then encoded by the offset of the next face. The size of a single integer is 4 bytes, so this reduces memory usage by 3 times. The same method is used for `CurvesGeometry`, so Blender already has an abstraction to simplify using these offsets called `OffsetIndices`. This class is used to easily retrieve a range of corner indices for each face. This also gives the opportunity for sharing some logic with curves. Another benefit of the change is that the offsets and sizes stored in `MPoly` can no longer disagree with each other. Storing faces in the order of their corners can simplify some code too. Face/polygon variables now use the `IndexRange` type, which comes with quite a few utilities that can simplify code. Some: - The offset integer array has to be one longer than the face count to avoid a branch for every face, which means the data is no longer part of the mesh's `CustomData`. - We lose the ability to "reference" an original mesh's offset array until more reusable CoW from #104478 is committed. That will be added in a separate commit. - Since they aren't part of `CustomData`, poly offsets often have to be copied manually. - To simplify using `OffsetIndices` in many places, some functions and structs in headers were moved to only compile in C++. - All meshes created by Blender use the same order for faces and face corners, but just in case, meshes with mismatched order are fixed by versioning code. - `MeshPolygon.totloop` is no longer editable in RNA. This API break is necessary here unfortunately. It should be worth it in 3.6, since that's the best way to allow loading meshes from 4.0, which is important for an LTS version. Pull Request: https://projects.blender.org/blender/blender/pulls/105938
2023-04-04 20:39:28 +02:00
if (mesh->face_offset_indices) {
BLO_write_int32_array(writer, mesh->faces_num + 1, mesh->face_offset_indices);
Mesh: Replace MPoly struct with offset indices Implements #95967. Currently the `MPoly` struct is 12 bytes, and stores the index of a face's first corner and the number of corners/verts/edges. Polygons and corners are always created in order by Blender, meaning each face's corners will be after the previous face's corners. We can take advantage of this fact and eliminate the redundancy in mesh face storage by only storing a single integer corner offset for each face. The size of the face is then encoded by the offset of the next face. The size of a single integer is 4 bytes, so this reduces memory usage by 3 times. The same method is used for `CurvesGeometry`, so Blender already has an abstraction to simplify using these offsets called `OffsetIndices`. This class is used to easily retrieve a range of corner indices for each face. This also gives the opportunity for sharing some logic with curves. Another benefit of the change is that the offsets and sizes stored in `MPoly` can no longer disagree with each other. Storing faces in the order of their corners can simplify some code too. Face/polygon variables now use the `IndexRange` type, which comes with quite a few utilities that can simplify code. Some: - The offset integer array has to be one longer than the face count to avoid a branch for every face, which means the data is no longer part of the mesh's `CustomData`. - We lose the ability to "reference" an original mesh's offset array until more reusable CoW from #104478 is committed. That will be added in a separate commit. - Since they aren't part of `CustomData`, poly offsets often have to be copied manually. - To simplify using `OffsetIndices` in many places, some functions and structs in headers were moved to only compile in C++. - All meshes created by Blender use the same order for faces and face corners, but just in case, meshes with mismatched order are fixed by versioning code. - `MeshPolygon.totloop` is no longer editable in RNA. This API break is necessary here unfortunately. It should be worth it in 3.6, since that's the best way to allow loading meshes from 4.0, which is important for an LTS version. Pull Request: https://projects.blender.org/blender/blender/pulls/105938
2023-04-04 20:39:28 +02:00
}
}
static void mesh_blend_read_data(BlendDataReader *reader, ID *id)
{
Mesh *mesh = reinterpret_cast<Mesh *>(id);
BLO_read_pointer_array(reader, (void **)&mesh->mat);
/* This check added for python created meshes. */
if (!mesh->mat) {
mesh->totcol = 0;
}
Mesh: Remove redundant custom data pointers For copy-on-write, we want to share attribute arrays between meshes where possible. Mutable pointers like `Mesh.mvert` make that difficult by making ownership vague. They also make code more complex by adding redundancy. The simplest solution is just removing them and retrieving layers from `CustomData` as needed. Similar changes have already been applied to curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of the pointers generally makes code more obvious and more reusable. Mesh data is now accessed with a C++ API (`Mesh::edges()` or `Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`). The CoW changes this commit makes possible are described in T95845 and T95842, and started in D14139 and D14140. The change also simplifies the ongoing mesh struct-of-array refactors from T95965. **RNA/Python Access Performance** Theoretically, accessing mesh elements with the RNA API may become slower, since the layer needs to be found on every random access. However, overhead is already high enough that this doesn't make a noticible differenc, and performance is actually improved in some cases. Random access can be up to 10% faster, but other situations might be a bit slower. Generally using `foreach_get/set` are the best way to improve performance. See the differential revision for more discussion about Python performance. Cycles has been updated to use raw pointers and the internal Blender mesh types, mostly because there is no sense in having this overhead when it's already compiled with Blender. In my tests this roughly halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million face grid). Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
/* 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. */
BLO_read_data_address(reader, &mesh->mvert);
BLO_read_data_address(reader, &mesh->medge);
BLO_read_data_address(reader, &mesh->mface);
BLO_read_data_address(reader, &mesh->mtface);
BLO_read_data_address(reader, &mesh->dvert);
Mesh: Remove redundant custom data pointers For copy-on-write, we want to share attribute arrays between meshes where possible. Mutable pointers like `Mesh.mvert` make that difficult by making ownership vague. They also make code more complex by adding redundancy. The simplest solution is just removing them and retrieving layers from `CustomData` as needed. Similar changes have already been applied to curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of the pointers generally makes code more obvious and more reusable. Mesh data is now accessed with a C++ API (`Mesh::edges()` or `Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`). The CoW changes this commit makes possible are described in T95845 and T95842, and started in D14139 and D14140. The change also simplifies the ongoing mesh struct-of-array refactors from T95965. **RNA/Python Access Performance** Theoretically, accessing mesh elements with the RNA API may become slower, since the layer needs to be found on every random access. However, overhead is already high enough that this doesn't make a noticible differenc, and performance is actually improved in some cases. Random access can be up to 10% faster, but other situations might be a bit slower. Generally using `foreach_get/set` are the best way to improve performance. See the differential revision for more discussion about Python performance. Cycles has been updated to use raw pointers and the internal Blender mesh types, mostly because there is no sense in having this overhead when it's already compiled with Blender. In my tests this roughly halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million face grid). Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
BLO_read_data_address(reader, &mesh->tface);
BLO_read_data_address(reader, &mesh->mcol);
BLO_read_data_address(reader, &mesh->mselect);
BLO_read_list(reader, &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);
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_data_address(reader, &mesh->active_color_attribute);
BLO_read_data_address(reader, &mesh->default_color_attribute);
mesh->texspace_flag &= ~ME_TEXSPACE_FLAG_AUTO_EVALUATED;
mesh->edit_mesh = nullptr;
mesh->runtime = new blender::bke::MeshRuntime();
if (mesh->face_offset_indices) {
BLO_read_int32_array(reader, mesh->faces_num + 1, &mesh->face_offset_indices);
mesh->runtime->face_offsets_sharing_info = blender::implicit_sharing::info_for_mem_free(
mesh->face_offset_indices);
}
if (mesh->mselect == nullptr) {
mesh->totselect = 0;
}
2021-07-16 11:45:52 +10:00
if (BLO_read_requires_endian_switch(reader) && mesh->tface) {
TFace *tf = mesh->tface;
for (int i = 0; i < mesh->totface_legacy; i++, tf++) {
BLI_endian_switch_uint32_array(tf->col, 4);
}
}
}
IDTypeInfo IDType_ID_ME = {
/*id_code*/ ID_ME,
/*id_filter*/ FILTER_ID_ME,
/*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,
/*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,
};
Mesh: Move positions to a generic attribute **Changes** As described in T93602, this patch removes all use of the `MVert` struct, replacing it with a generic named attribute with the name `"position"`, consistent with other geometry types. Variable names have been changed from `verts` to `positions`, to align with the attribute name and the more generic design (positions are not vertices, they are just an attribute stored on the point domain). This change is made possible by previous commits that moved all other data out of `MVert` to runtime data or other generic attributes. What remains is mostly a simple type change. Though, the type still shows up 859 times, so the patch is quite large. One compromise is that now `CD_MASK_BAREMESH` now contains `CD_PROP_FLOAT3`. With the general move towards generic attributes over custom data types, we are removing use of these type masks anyway. **Benefits** The most obvious benefit is reduced memory usage and the benefits that brings in memory-bound situations. `float3` is only 3 bytes, in comparison to `MVert` which was 4. When there are millions of vertices this starts to matter more. The other benefits come from using a more generic type. Instead of writing algorithms specifically for `MVert`, code can just use arrays of vectors. This will allow eliminating many temporary arrays or wrappers used to extract positions. Many possible improvements aren't implemented in this patch, though I did switch simplify or remove the process of creating temporary position arrays in a few places. The design clarity that "positions are just another attribute" brings allows removing explicit copying of vertices in some procedural operations-- they are just processed like most other attributes. **Performance** This touches so many areas that it's hard to benchmark exhaustively, but I observed some areas as examples. * The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster. * The Spring splash screen went from ~4.3 to ~4.5 fps. * The subdivision surface modifier/node was slightly faster RNA access through Python may be slightly slower, since now we need a name lookup instead of just a custom data type lookup for each index. **Future Improvements** * Remove uses of "vert_coords" functions: * `BKE_mesh_vert_coords_alloc` * `BKE_mesh_vert_coords_get` * `BKE_mesh_vert_coords_apply{_with_mat4}` * Remove more hidden copying of positions * General simplification now possible in many areas * Convert more code to C++ to use `float3` instead of `float[3]` * Currently `reinterpret_cast` is used for those C-API functions Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
bool BKE_mesh_attribute_required(const char *name)
{
Mesh: Move edges to a generic attribute Implements #95966, as the final step of #95965. This commit changes the storage of mesh edge vertex indices from the `MEdge` type to the generic `int2` attribute type. This follows the general design for geometry and the attribute system, where the data storage type and the usage semantics are separated. The main benefit of the change is reduced memory usage-- the requirements of storing mesh edges is reduced by 1/3. For example, this saves 8MB on a 1 million vertex grid. This also gives performance benefits to any memory-bound mesh processing algorithm that uses edges. Another benefit is that all of the edge's vertex indices are contiguous. In a few cases, it's helpful to process all of them as `Span<int>` rather than `Span<int2>`. Similarly, the type is more likely to match a generic format used by a library, or code that shouldn't know about specific Blender `Mesh` types. Various Notes: - The `.edge_verts` name is used to reflect a mapping between domains, similar to `.corner_verts`, etc. The period means that it the data shouldn't change arbitrarily by the user or procedural operations. - `edge[0]` is now used instead of `edge.v1` - Signed integers are used instead of unsigned to reduce the mixing of signed-ness, which can be error prone. - All of the previously used core mesh data types (`MVert`, `MEdge`, `MLoop`, `MPoly` are now deprecated. Only generic types are used). - The `vec2i` DNA type is used in the few C files where necessary. Pull Request: https://projects.blender.org/blender/blender/pulls/106638
2023-04-17 13:47:41 +02:00
return ELEM(StringRef(name), "position", ".corner_vert", ".corner_edge", ".edge_verts");
Mesh: Move positions to a generic attribute **Changes** As described in T93602, this patch removes all use of the `MVert` struct, replacing it with a generic named attribute with the name `"position"`, consistent with other geometry types. Variable names have been changed from `verts` to `positions`, to align with the attribute name and the more generic design (positions are not vertices, they are just an attribute stored on the point domain). This change is made possible by previous commits that moved all other data out of `MVert` to runtime data or other generic attributes. What remains is mostly a simple type change. Though, the type still shows up 859 times, so the patch is quite large. One compromise is that now `CD_MASK_BAREMESH` now contains `CD_PROP_FLOAT3`. With the general move towards generic attributes over custom data types, we are removing use of these type masks anyway. **Benefits** The most obvious benefit is reduced memory usage and the benefits that brings in memory-bound situations. `float3` is only 3 bytes, in comparison to `MVert` which was 4. When there are millions of vertices this starts to matter more. The other benefits come from using a more generic type. Instead of writing algorithms specifically for `MVert`, code can just use arrays of vectors. This will allow eliminating many temporary arrays or wrappers used to extract positions. Many possible improvements aren't implemented in this patch, though I did switch simplify or remove the process of creating temporary position arrays in a few places. The design clarity that "positions are just another attribute" brings allows removing explicit copying of vertices in some procedural operations-- they are just processed like most other attributes. **Performance** This touches so many areas that it's hard to benchmark exhaustively, but I observed some areas as examples. * The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster. * The Spring splash screen went from ~4.3 to ~4.5 fps. * The subdivision surface modifier/node was slightly faster RNA access through Python may be slightly slower, since now we need a name lookup instead of just a custom data type lookup for each index. **Future Improvements** * Remove uses of "vert_coords" functions: * `BKE_mesh_vert_coords_alloc` * `BKE_mesh_vert_coords_get` * `BKE_mesh_vert_coords_apply{_with_mat4}` * Remove more hidden copying of positions * General simplification now possible in many areas * Convert more code to C++ to use `float3` instead of `float[3]` * Currently `reinterpret_cast` is used for those C-API functions Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
}
void BKE_mesh_ensure_skin_customdata(Mesh *mesh)
{
BMesh *bm = mesh->edit_mesh ? mesh->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 = (MVertSkin *)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 = (MVertSkin *)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->edit_mesh) {
return CustomData_has_layer(&mesh->edit_mesh->bm->ldata, CD_CUSTOMLOOPNORMAL);
}
return CustomData_has_layer(&mesh->corner_data, CD_CUSTOMLOOPNORMAL);
}
void BKE_mesh_free_data_for_undo(Mesh *mesh)
2002-10-12 11:37:38 +00:00
{
mesh_free_data(&mesh->id);
}
/**
* \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)
2023-04-12 11:49:30 -04:00
* Since edit-mesh is tied to the object's mode, which crashes when called in edit-mode.
* See: #90972.
*/
2023-04-12 11:49:30 -04:00
static void mesh_clear_geometry(Mesh &mesh)
{
CustomData_free(&mesh.vert_data, mesh.verts_num);
CustomData_free(&mesh.edge_data, mesh.edges_num);
CustomData_free(&mesh.fdata_legacy, mesh.totface_legacy);
CustomData_free(&mesh.corner_data, mesh.corners_num);
CustomData_free(&mesh.face_data, mesh.faces_num);
if (mesh.face_offset_indices) {
blender::implicit_sharing::free_shared_data(&mesh.face_offset_indices,
&mesh.runtime->face_offsets_sharing_info);
}
2023-04-12 11:49:30 -04:00
MEM_SAFE_FREE(mesh.mselect);
mesh.verts_num = 0;
mesh.edges_num = 0;
mesh.totface_legacy = 0;
mesh.corners_num = 0;
mesh.faces_num = 0;
2023-04-12 11:49:30 -04:00
mesh.act_face = -1;
mesh.totselect = 0;
}
static void clear_attribute_names(Mesh &mesh)
{
BLI_freelistN(&mesh.vertex_group_names);
MEM_SAFE_FREE(mesh.active_color_attribute);
MEM_SAFE_FREE(mesh.default_color_attribute);
2002-10-12 11:37:38 +00:00
}
void BKE_mesh_clear_geometry(Mesh *mesh)
{
BKE_mesh_runtime_clear_cache(mesh);
2023-04-12 11:49:30 -04:00
mesh_clear_geometry(*mesh);
}
void BKE_mesh_clear_geometry_and_metadata(Mesh *mesh)
{
BKE_mesh_runtime_clear_cache(mesh);
2023-04-12 11:49:30 -04:00
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, mesh->totface_legacy);
}
else {
CustomData_reset(&mesh->fdata_legacy);
}
mesh->totface_legacy = 0;
}
Mesh *BKE_mesh_add(Main *bmain, const char *name)
{
return static_cast<Mesh *>(BKE_id_new(bmain, ID_ME, name));
2002-10-12 11:37:38 +00:00
}
void BKE_mesh_face_offsets_ensure_alloc(Mesh *mesh)
Mesh: Replace MPoly struct with offset indices Implements #95967. Currently the `MPoly` struct is 12 bytes, and stores the index of a face's first corner and the number of corners/verts/edges. Polygons and corners are always created in order by Blender, meaning each face's corners will be after the previous face's corners. We can take advantage of this fact and eliminate the redundancy in mesh face storage by only storing a single integer corner offset for each face. The size of the face is then encoded by the offset of the next face. The size of a single integer is 4 bytes, so this reduces memory usage by 3 times. The same method is used for `CurvesGeometry`, so Blender already has an abstraction to simplify using these offsets called `OffsetIndices`. This class is used to easily retrieve a range of corner indices for each face. This also gives the opportunity for sharing some logic with curves. Another benefit of the change is that the offsets and sizes stored in `MPoly` can no longer disagree with each other. Storing faces in the order of their corners can simplify some code too. Face/polygon variables now use the `IndexRange` type, which comes with quite a few utilities that can simplify code. Some: - The offset integer array has to be one longer than the face count to avoid a branch for every face, which means the data is no longer part of the mesh's `CustomData`. - We lose the ability to "reference" an original mesh's offset array until more reusable CoW from #104478 is committed. That will be added in a separate commit. - Since they aren't part of `CustomData`, poly offsets often have to be copied manually. - To simplify using `OffsetIndices` in many places, some functions and structs in headers were moved to only compile in C++. - All meshes created by Blender use the same order for faces and face corners, but just in case, meshes with mismatched order are fixed by versioning code. - `MeshPolygon.totloop` is no longer editable in RNA. This API break is necessary here unfortunately. It should be worth it in 3.6, since that's the best way to allow loading meshes from 4.0, which is important for an LTS version. Pull Request: https://projects.blender.org/blender/blender/pulls/105938
2023-04-04 20:39:28 +02:00
{
BLI_assert(mesh->face_offset_indices == nullptr);
BLI_assert(mesh->runtime->face_offsets_sharing_info == nullptr);
if (mesh->faces_num == 0) {
Mesh: Replace MPoly struct with offset indices Implements #95967. Currently the `MPoly` struct is 12 bytes, and stores the index of a face's first corner and the number of corners/verts/edges. Polygons and corners are always created in order by Blender, meaning each face's corners will be after the previous face's corners. We can take advantage of this fact and eliminate the redundancy in mesh face storage by only storing a single integer corner offset for each face. The size of the face is then encoded by the offset of the next face. The size of a single integer is 4 bytes, so this reduces memory usage by 3 times. The same method is used for `CurvesGeometry`, so Blender already has an abstraction to simplify using these offsets called `OffsetIndices`. This class is used to easily retrieve a range of corner indices for each face. This also gives the opportunity for sharing some logic with curves. Another benefit of the change is that the offsets and sizes stored in `MPoly` can no longer disagree with each other. Storing faces in the order of their corners can simplify some code too. Face/polygon variables now use the `IndexRange` type, which comes with quite a few utilities that can simplify code. Some: - The offset integer array has to be one longer than the face count to avoid a branch for every face, which means the data is no longer part of the mesh's `CustomData`. - We lose the ability to "reference" an original mesh's offset array until more reusable CoW from #104478 is committed. That will be added in a separate commit. - Since they aren't part of `CustomData`, poly offsets often have to be copied manually. - To simplify using `OffsetIndices` in many places, some functions and structs in headers were moved to only compile in C++. - All meshes created by Blender use the same order for faces and face corners, but just in case, meshes with mismatched order are fixed by versioning code. - `MeshPolygon.totloop` is no longer editable in RNA. This API break is necessary here unfortunately. It should be worth it in 3.6, since that's the best way to allow loading meshes from 4.0, which is important for an LTS version. Pull Request: https://projects.blender.org/blender/blender/pulls/105938
2023-04-04 20:39:28 +02:00
return;
}
mesh->face_offset_indices = static_cast<int *>(
MEM_malloc_arrayN(mesh->faces_num + 1, sizeof(int), __func__));
mesh->runtime->face_offsets_sharing_info = blender::implicit_sharing::info_for_mem_free(
mesh->face_offset_indices);
#ifndef NDEBUG
Mesh: Replace MPoly struct with offset indices Implements #95967. Currently the `MPoly` struct is 12 bytes, and stores the index of a face's first corner and the number of corners/verts/edges. Polygons and corners are always created in order by Blender, meaning each face's corners will be after the previous face's corners. We can take advantage of this fact and eliminate the redundancy in mesh face storage by only storing a single integer corner offset for each face. The size of the face is then encoded by the offset of the next face. The size of a single integer is 4 bytes, so this reduces memory usage by 3 times. The same method is used for `CurvesGeometry`, so Blender already has an abstraction to simplify using these offsets called `OffsetIndices`. This class is used to easily retrieve a range of corner indices for each face. This also gives the opportunity for sharing some logic with curves. Another benefit of the change is that the offsets and sizes stored in `MPoly` can no longer disagree with each other. Storing faces in the order of their corners can simplify some code too. Face/polygon variables now use the `IndexRange` type, which comes with quite a few utilities that can simplify code. Some: - The offset integer array has to be one longer than the face count to avoid a branch for every face, which means the data is no longer part of the mesh's `CustomData`. - We lose the ability to "reference" an original mesh's offset array until more reusable CoW from #104478 is committed. That will be added in a separate commit. - Since they aren't part of `CustomData`, poly offsets often have to be copied manually. - To simplify using `OffsetIndices` in many places, some functions and structs in headers were moved to only compile in C++. - All meshes created by Blender use the same order for faces and face corners, but just in case, meshes with mismatched order are fixed by versioning code. - `MeshPolygon.totloop` is no longer editable in RNA. This API break is necessary here unfortunately. It should be worth it in 3.6, since that's the best way to allow loading meshes from 4.0, which is important for an LTS version. Pull Request: https://projects.blender.org/blender/blender/pulls/105938
2023-04-04 20:39:28 +02:00
/* Fill offsets with obviously bad values to simplify finding missing initialization. */
mesh->face_offsets_for_write().fill(-1);
Mesh: Replace MPoly struct with offset indices Implements #95967. Currently the `MPoly` struct is 12 bytes, and stores the index of a face's first corner and the number of corners/verts/edges. Polygons and corners are always created in order by Blender, meaning each face's corners will be after the previous face's corners. We can take advantage of this fact and eliminate the redundancy in mesh face storage by only storing a single integer corner offset for each face. The size of the face is then encoded by the offset of the next face. The size of a single integer is 4 bytes, so this reduces memory usage by 3 times. The same method is used for `CurvesGeometry`, so Blender already has an abstraction to simplify using these offsets called `OffsetIndices`. This class is used to easily retrieve a range of corner indices for each face. This also gives the opportunity for sharing some logic with curves. Another benefit of the change is that the offsets and sizes stored in `MPoly` can no longer disagree with each other. Storing faces in the order of their corners can simplify some code too. Face/polygon variables now use the `IndexRange` type, which comes with quite a few utilities that can simplify code. Some: - The offset integer array has to be one longer than the face count to avoid a branch for every face, which means the data is no longer part of the mesh's `CustomData`. - We lose the ability to "reference" an original mesh's offset array until more reusable CoW from #104478 is committed. That will be added in a separate commit. - Since they aren't part of `CustomData`, poly offsets often have to be copied manually. - To simplify using `OffsetIndices` in many places, some functions and structs in headers were moved to only compile in C++. - All meshes created by Blender use the same order for faces and face corners, but just in case, meshes with mismatched order are fixed by versioning code. - `MeshPolygon.totloop` is no longer editable in RNA. This API break is necessary here unfortunately. It should be worth it in 3.6, since that's the best way to allow loading meshes from 4.0, which is important for an LTS version. Pull Request: https://projects.blender.org/blender/blender/pulls/105938
2023-04-04 20:39:28 +02:00
#endif
/* Set common values for convenience. */
mesh->face_offset_indices[0] = 0;
mesh->face_offset_indices[mesh->faces_num] = mesh->corners_num;
Mesh: Replace MPoly struct with offset indices Implements #95967. Currently the `MPoly` struct is 12 bytes, and stores the index of a face's first corner and the number of corners/verts/edges. Polygons and corners are always created in order by Blender, meaning each face's corners will be after the previous face's corners. We can take advantage of this fact and eliminate the redundancy in mesh face storage by only storing a single integer corner offset for each face. The size of the face is then encoded by the offset of the next face. The size of a single integer is 4 bytes, so this reduces memory usage by 3 times. The same method is used for `CurvesGeometry`, so Blender already has an abstraction to simplify using these offsets called `OffsetIndices`. This class is used to easily retrieve a range of corner indices for each face. This also gives the opportunity for sharing some logic with curves. Another benefit of the change is that the offsets and sizes stored in `MPoly` can no longer disagree with each other. Storing faces in the order of their corners can simplify some code too. Face/polygon variables now use the `IndexRange` type, which comes with quite a few utilities that can simplify code. Some: - The offset integer array has to be one longer than the face count to avoid a branch for every face, which means the data is no longer part of the mesh's `CustomData`. - We lose the ability to "reference" an original mesh's offset array until more reusable CoW from #104478 is committed. That will be added in a separate commit. - Since they aren't part of `CustomData`, poly offsets often have to be copied manually. - To simplify using `OffsetIndices` in many places, some functions and structs in headers were moved to only compile in C++. - All meshes created by Blender use the same order for faces and face corners, but just in case, meshes with mismatched order are fixed by versioning code. - `MeshPolygon.totloop` is no longer editable in RNA. This API break is necessary here unfortunately. It should be worth it in 3.6, since that's the best way to allow loading meshes from 4.0, which is important for an LTS version. Pull Request: https://projects.blender.org/blender/blender/pulls/105938
2023-04-04 20:39:28 +02:00
}
Span<float3> Mesh::vert_positions() const
{
return {static_cast<const float3 *>(
CustomData_get_layer_named(&this->vert_data, CD_PROP_FLOAT3, "position")),
this->verts_num};
}
MutableSpan<float3> Mesh::vert_positions_for_write()
{
return {static_cast<float3 *>(CustomData_get_layer_named_for_write(
&this->vert_data, CD_PROP_FLOAT3, "position", this->verts_num)),
this->verts_num};
}
Span<int2> Mesh::edges() const
{
return {static_cast<const int2 *>(
CustomData_get_layer_named(&this->edge_data, CD_PROP_INT32_2D, ".edge_verts")),
this->edges_num};
}
MutableSpan<int2> Mesh::edges_for_write()
{
return {static_cast<int2 *>(CustomData_get_layer_named_for_write(
&this->edge_data, CD_PROP_INT32_2D, ".edge_verts", this->edges_num)),
this->edges_num};
}
OffsetIndices<int> Mesh::faces() const
{
return Span(this->face_offset_indices, this->faces_num + 1);
}
Span<int> Mesh::face_offsets() const
{
if (this->faces_num == 0) {
return {};
}
return {this->face_offset_indices, this->faces_num + 1};
}
MutableSpan<int> Mesh::face_offsets_for_write()
{
if (this->faces_num == 0) {
return {};
}
blender::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<int> Mesh::corner_verts() const
{
return {static_cast<const int *>(
CustomData_get_layer_named(&this->corner_data, CD_PROP_INT32, ".corner_vert")),
this->corners_num};
}
MutableSpan<int> Mesh::corner_verts_for_write()
{
return {static_cast<int *>(CustomData_get_layer_named_for_write(
&this->corner_data, CD_PROP_INT32, ".corner_vert", this->corners_num)),
this->corners_num};
}
Span<int> Mesh::corner_edges() const
{
return {static_cast<const int *>(
CustomData_get_layer_named(&this->corner_data, CD_PROP_INT32, ".corner_edge")),
this->corners_num};
}
MutableSpan<int> Mesh::corner_edges_for_write()
{
return {static_cast<int *>(CustomData_get_layer_named_for_write(
&this->corner_data, CD_PROP_INT32, ".corner_edge", this->corners_num)),
this->corners_num};
}
Span<MDeformVert> Mesh::deform_verts() const
{
const MDeformVert *dverts = static_cast<const MDeformVert *>(
CustomData_get_layer(&this->vert_data, CD_MDEFORMVERT));
if (!dverts) {
return {};
}
return {dverts, this->verts_num};
}
MutableSpan<MDeformVert> Mesh::deform_verts_for_write()
{
MDeformVert *dvert = static_cast<MDeformVert *>(
CustomData_get_layer_for_write(&this->vert_data, CD_MDEFORMVERT, this->verts_num));
if (dvert) {
return {dvert, this->verts_num};
}
return {static_cast<MDeformVert *>(CustomData_add_layer(
&this->vert_data, CD_MDEFORMVERT, CD_SET_DEFAULT, this->verts_num)),
this->verts_num};
}
namespace blender::bke {
void mesh_ensure_default_color_attribute_on_add(Mesh &mesh,
const AttributeIDRef &id,
AttrDomain domain,
eCustomDataType data_type)
{
if (id.is_anonymous()) {
return;
}
if (!(CD_TYPE_AS_MASK(data_type) & CD_MASK_COLOR_ALL) ||
!(ATTR_DOMAIN_AS_MASK(domain) & ATTR_DOMAIN_MASK_COLOR))
{
return;
}
if (mesh.default_color_attribute) {
return;
}
mesh.default_color_attribute = BLI_strdupn(id.name().data(), id.name().size());
}
2023-04-12 11:49:30 -04:00
static void mesh_ensure_cdlayers_primary(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, CD_PROP_FLOAT3, attribute_init);
attributes.add(".edge_verts", AttrDomain::Edge, CD_PROP_INT32_2D, attribute_init);
attributes.add(".corner_vert", AttrDomain::Corner, CD_PROP_INT32, attribute_init);
attributes.add(".corner_edge", AttrDomain::Corner, CD_PROP_INT32, attribute_init);
}
} // namespace blender::bke
Mesh *BKE_mesh_new_nomain(const int verts_num,
const int edges_num,
const int faces_num,
const int corners_num)
{
Mesh *mesh = static_cast<Mesh *>(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;
blender::bke::mesh_ensure_cdlayers_primary(*mesh);
BKE_mesh_face_offsets_ensure_alloc(mesh);
return mesh;
}
static void copy_attribute_names(const Mesh &mesh_src, Mesh &mesh_dst)
{
if (mesh_src.active_color_attribute) {
MEM_SAFE_FREE(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_FREE(mesh_dst.default_color_attribute);
mesh_dst.default_color_attribute = BLI_strdup(mesh_src.default_color_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;
2020-03-05 14:53:23 +01:00
me_dst->face_sets_color_seed = me_src->face_sets_color_seed;
me_dst->face_sets_color_default = me_src->face_sets_color_default;
2020-03-05 14:53:23 +01:00
/* 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 & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE));
BKE_mesh_copy_parameters(me_dst, me_src);
copy_attribute_names(*me_src, *me_dst);
/* Copy vertex group names. */
BLI_assert(BLI_listbase_is_empty(&me_dst->vertex_group_names));
BKE_defgroup_copy_list(&me_dst->vertex_group_names, &me_src->vertex_group_names);
/* Copy materials. */
if (me_dst->mat != nullptr) {
MEM_freeN(me_dst->mat);
}
me_dst->mat = (Material **)MEM_dupallocN(me_src->mat);
me_dst->totcol = me_src->totcol;
}
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,
2024-01-19 12:08:40 -05:00
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 = static_cast<Mesh *>(BKE_id_new_nomain(ID_ME, nullptr));
me_dst->mselect = (MSelect *)MEM_dupallocN(me_src->mselect);
me_dst->verts_num = verts_num;
me_dst->edges_num = edges_num;
me_dst->faces_num = faces_num;
2024-01-19 12:08:40 -05:00
me_dst->corners_num = corners_num;
me_dst->totface_legacy = tessface_num;
BKE_mesh_copy_parameters_for_eval(me_dst, me_src);
CustomData_copy_layout(
&me_src->vert_data, &me_dst->vert_data, mask.vmask, CD_SET_DEFAULT, verts_num);
CustomData_copy_layout(
&me_src->edge_data, &me_dst->edge_data, mask.emask, CD_SET_DEFAULT, edges_num);
CustomData_copy_layout(
&me_src->face_data, &me_dst->face_data, mask.pmask, CD_SET_DEFAULT, faces_num);
CustomData_copy_layout(
2024-01-19 12:08:40 -05:00
&me_src->corner_data, &me_dst->corner_data, mask.lmask, CD_SET_DEFAULT, corners_num);
if (do_tessface) {
Custom Data: support implicit sharing for custom data layers This integrates the new implicit-sharing system (from fbcddfcd68adc72f) with `CustomData`. Now the potentially long arrays referenced by custom data layers can be shared between different systems but most importantly between different geometries. This makes e.g. copying a mesh much cheaper because none of the attributes has to be copied. Only when an attribute is modified does it have to be copied. Also see the original design task: #95845. This reduces memory and improves performance by avoiding unnecessary data copies. For example, the used memory after loading a highly subdivided mesh is reduced from 2.4GB to 1.79GB. This is about 25% less which is the expected amount because in `main` there are 4 copies of the data: 1. The original data which is allocated when the file is loaded. 2. The copy for the depsgraph allocated during depsgraph evaluation. 3. The copy for the undo system allocated when the first undo step is created right after loading the file. 4. GPU buffers allocated for drawing. This patch only gets rid of copy number 2 for the depsgraph. In theory the other copies can be removed as part of follow up PRs as well though. ----- The patch has three main components: * Slightly modified `CustomData` API to make it work better with implicit sharing: * `CD_REFERENCE` and `CD_DUPLICATE` have been removed because they are meaningless when implicit-sharing is used. * `CD_ASSIGN` has been removed as well because it's not an allocation type anyway. The functionality of using existing arrays as custom data layers has not been removed though. * This can still be done with `CustomData_add_layer_with_data` which also has a new argument that allows passing in information about whether the array is shared. * `CD_FLAG_NOFREE` has been removed because it's no longer necessary. It only existed because of `CD_REFERENCE`. * `CustomData_copy` and `CustomData_merge` have been split up into a functions that do copy the actual attribute values and those that do not. The latter functions now have the `_layout` suffix (e.g. `CustomData_copy_layout`). * Changes in `customdata.cc` to make it actually use implicit-sharing. * Changes in various other files to adapt to the changes in `BKE_customdata.h`. Pull Request: https://projects.blender.org/blender/blender/pulls/106228
2023-04-13 14:57:57 +02:00
CustomData_copy_layout(
&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. */
blender::bke::mesh_ensure_cdlayers_primary(*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);
}
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,
2024-01-19 12:08:40 -05:00
const int corners_num)
{
return BKE_mesh_new_nomain_from_template_ex(
2024-01-19 12:08:40 -05:00
me_src, verts_num, edges_num, 0, faces_num, corners_num, CD_MASK_EVERYTHING);
}
void BKE_mesh_eval_delete(Mesh *mesh_eval)
{
/* Evaluated mesh may point to edit mesh, but never owns it. */
mesh_eval->edit_mesh = nullptr;
mesh_free_data(&mesh_eval->id);
BKE_libblock_free_data(&mesh_eval->id, false);
MEM_freeN(mesh_eval);
}
Mesh *BKE_mesh_copy_for_eval(const Mesh *source)
{
return reinterpret_cast<Mesh *>(
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,
Object *ob,
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 = ob->shapenr;
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 = static_cast<Mesh *>(BKE_id_new_nomain(ID_ME, 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 = static_cast<Mesh *>(BKE_id_new_nomain(ID_ME, nullptr));
BM_mesh_bm_to_me_for_eval(*bm, *mesh, cd_mask_extra);
BKE_mesh_copy_parameters_for_eval(mesh, me_settings);
return mesh;
}
Mesh: Avoid creating incorrect original index layers Currently, whenever any BMesh is converted to a Mesh (except for edit mode switching), original index (`CD_ORIGINDEX`) layers are added. This is incorrect, because many operations just convert some Mesh into a BMesh and then back, but they shouldn't make any assumption about where their input mesh came from. It might even come from a primitive in geometry nodes, where there are no original indices at all. Conceptually, mesh original indices should be filled by the modifier stack when first creating the evaluated mesh. So that's where they're moved in this patch. A separate function now fills the indices with their default (0,1,2,3...) values. The way the mesh wrapper system defers the BMesh to Mesh conversion makes this a bit less obvious though. The old behavior is incorrect, but it's also slower, because three arrays the size of the mesh's vertices, edges, and faces had to be allocated and filled during the BMesh to Mesh conversion, which just ends up putting more pressure on the cache. In the many cases where original indices aren't used, I measured an **8% speedup** for the conversion (from 76.5ms to 70.7ms). Generally there is an assumption that BMesh is "original" and Mesh is "evaluated". After this patch, that assumption isn't quite as strong, but it still exists for two reasons. First, original indices are added whenever converting a BMesh "wrapper" to a Mesh. Second, original indices are not added to the BMesh at the beginning of evaluation, which assumes that every BMesh in the viewport is original and doesn't need the mapping. Differential Revision: https://developer.blender.org/D14018
2022-02-18 10:51:00 -06:00
static void ensure_orig_index_layer(CustomData &data, const int size)
{
if (CustomData_has_layer(&data, CD_ORIGINDEX)) {
return;
}
int *indices = (int *)CustomData_add_layer(&data, CD_ORIGINDEX, CD_SET_DEFAULT, size);
Mesh: Avoid creating incorrect original index layers Currently, whenever any BMesh is converted to a Mesh (except for edit mode switching), original index (`CD_ORIGINDEX`) layers are added. This is incorrect, because many operations just convert some Mesh into a BMesh and then back, but they shouldn't make any assumption about where their input mesh came from. It might even come from a primitive in geometry nodes, where there are no original indices at all. Conceptually, mesh original indices should be filled by the modifier stack when first creating the evaluated mesh. So that's where they're moved in this patch. A separate function now fills the indices with their default (0,1,2,3...) values. The way the mesh wrapper system defers the BMesh to Mesh conversion makes this a bit less obvious though. The old behavior is incorrect, but it's also slower, because three arrays the size of the mesh's vertices, edges, and faces had to be allocated and filled during the BMesh to Mesh conversion, which just ends up putting more pressure on the cache. In the many cases where original indices aren't used, I measured an **8% speedup** for the conversion (from 76.5ms to 70.7ms). Generally there is an assumption that BMesh is "original" and Mesh is "evaluated". After this patch, that assumption isn't quite as strong, but it still exists for two reasons. First, original indices are added whenever converting a BMesh "wrapper" to a Mesh. Second, original indices are not added to the BMesh at the beginning of evaluation, which assumes that every BMesh in the viewport is original and doesn't need the mapping. Differential Revision: https://developer.blender.org/D14018
2022-02-18 10:51:00 -06:00
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);
Mesh: Avoid creating incorrect original index layers Currently, whenever any BMesh is converted to a Mesh (except for edit mode switching), original index (`CD_ORIGINDEX`) layers are added. This is incorrect, because many operations just convert some Mesh into a BMesh and then back, but they shouldn't make any assumption about where their input mesh came from. It might even come from a primitive in geometry nodes, where there are no original indices at all. Conceptually, mesh original indices should be filled by the modifier stack when first creating the evaluated mesh. So that's where they're moved in this patch. A separate function now fills the indices with their default (0,1,2,3...) values. The way the mesh wrapper system defers the BMesh to Mesh conversion makes this a bit less obvious though. The old behavior is incorrect, but it's also slower, because three arrays the size of the mesh's vertices, edges, and faces had to be allocated and filled during the BMesh to Mesh conversion, which just ends up putting more pressure on the cache. In the many cases where original indices aren't used, I measured an **8% speedup** for the conversion (from 76.5ms to 70.7ms). Generally there is an assumption that BMesh is "original" and Mesh is "evaluated". After this patch, that assumption isn't quite as strong, but it still exists for two reasons. First, original indices are added whenever converting a BMesh "wrapper" to a Mesh. Second, original indices are not added to the BMesh at the beginning of evaluation, which assumes that every BMesh in the viewport is original and doesn't need the mapping. Differential Revision: https://developer.blender.org/D14018
2022-02-18 10:51:00 -06:00
}
void BKE_mesh_texspace_calc(Mesh *mesh)
{
using namespace blender;
if (mesh->texspace_flag & ME_TEXSPACE_FLAG_AUTO) {
const Bounds<float3> 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);
2002-10-12 11:37:38 +00:00
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;
2002-10-12 11:37:38 +00:00
}
}
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;
}
}
float (*BKE_mesh_orco_verts_get(Object *ob))[3]
2002-10-12 11:37:38 +00:00
{
Mesh *mesh = static_cast<Mesh *>(ob->data);
Mesh *tme = mesh->texcomesh ? mesh->texcomesh : mesh;
/* Get appropriate vertex coordinates */
float(*vcos)[3] = (float(*)[3])MEM_calloc_arrayN(mesh->verts_num, sizeof(*vcos), "orco mesh");
Mesh: Move positions to a generic attribute **Changes** As described in T93602, this patch removes all use of the `MVert` struct, replacing it with a generic named attribute with the name `"position"`, consistent with other geometry types. Variable names have been changed from `verts` to `positions`, to align with the attribute name and the more generic design (positions are not vertices, they are just an attribute stored on the point domain). This change is made possible by previous commits that moved all other data out of `MVert` to runtime data or other generic attributes. What remains is mostly a simple type change. Though, the type still shows up 859 times, so the patch is quite large. One compromise is that now `CD_MASK_BAREMESH` now contains `CD_PROP_FLOAT3`. With the general move towards generic attributes over custom data types, we are removing use of these type masks anyway. **Benefits** The most obvious benefit is reduced memory usage and the benefits that brings in memory-bound situations. `float3` is only 3 bytes, in comparison to `MVert` which was 4. When there are millions of vertices this starts to matter more. The other benefits come from using a more generic type. Instead of writing algorithms specifically for `MVert`, code can just use arrays of vectors. This will allow eliminating many temporary arrays or wrappers used to extract positions. Many possible improvements aren't implemented in this patch, though I did switch simplify or remove the process of creating temporary position arrays in a few places. The design clarity that "positions are just another attribute" brings allows removing explicit copying of vertices in some procedural operations-- they are just processed like most other attributes. **Performance** This touches so many areas that it's hard to benchmark exhaustively, but I observed some areas as examples. * The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster. * The Spring splash screen went from ~4.3 to ~4.5 fps. * The subdivision surface modifier/node was slightly faster RNA access through Python may be slightly slower, since now we need a name lookup instead of just a custom data type lookup for each index. **Future Improvements** * Remove uses of "vert_coords" functions: * `BKE_mesh_vert_coords_alloc` * `BKE_mesh_vert_coords_get` * `BKE_mesh_vert_coords_apply{_with_mat4}` * Remove more hidden copying of positions * General simplification now possible in many areas * Convert more code to C++ to use `float3` instead of `float[3]` * Currently `reinterpret_cast` is used for those C-API functions Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
const Span<float3> positions = tme->vert_positions();
Mesh: Remove redundant custom data pointers For copy-on-write, we want to share attribute arrays between meshes where possible. Mutable pointers like `Mesh.mvert` make that difficult by making ownership vague. They also make code more complex by adding redundancy. The simplest solution is just removing them and retrieving layers from `CustomData` as needed. Similar changes have already been applied to curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of the pointers generally makes code more obvious and more reusable. Mesh data is now accessed with a C++ API (`Mesh::edges()` or `Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`). The CoW changes this commit makes possible are described in T95845 and T95842, and started in D14139 and D14140. The change also simplifies the ongoing mesh struct-of-array refactors from T95965. **RNA/Python Access Performance** Theoretically, accessing mesh elements with the RNA API may become slower, since the layer needs to be found on every random access. However, overhead is already high enough that this doesn't make a noticible differenc, and performance is actually improved in some cases. Random access can be up to 10% faster, but other situations might be a bit slower. Generally using `foreach_get/set` are the best way to improve performance. See the differential revision for more discussion about Python performance. Cycles has been updated to use raw pointers and the internal Blender mesh types, mostly because there is no sense in having this overhead when it's already compiled with Blender. In my tests this roughly halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million face grid). Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
int totvert = min_ii(tme->verts_num, mesh->verts_num);
Mesh: Remove redundant custom data pointers For copy-on-write, we want to share attribute arrays between meshes where possible. Mutable pointers like `Mesh.mvert` make that difficult by making ownership vague. They also make code more complex by adding redundancy. The simplest solution is just removing them and retrieving layers from `CustomData` as needed. Similar changes have already been applied to curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of the pointers generally makes code more obvious and more reusable. Mesh data is now accessed with a C++ API (`Mesh::edges()` or `Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`). The CoW changes this commit makes possible are described in T95845 and T95842, and started in D14139 and D14140. The change also simplifies the ongoing mesh struct-of-array refactors from T95965. **RNA/Python Access Performance** Theoretically, accessing mesh elements with the RNA API may become slower, since the layer needs to be found on every random access. However, overhead is already high enough that this doesn't make a noticible differenc, and performance is actually improved in some cases. Random access can be up to 10% faster, but other situations might be a bit slower. Generally using `foreach_get/set` are the best way to improve performance. See the differential revision for more discussion about Python performance. Cycles has been updated to use raw pointers and the internal Blender mesh types, mostly because there is no sense in having this overhead when it's already compiled with Blender. In my tests this roughly halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million face grid). Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
for (int a = 0; a < totvert; a++) {
Mesh: Move positions to a generic attribute **Changes** As described in T93602, this patch removes all use of the `MVert` struct, replacing it with a generic named attribute with the name `"position"`, consistent with other geometry types. Variable names have been changed from `verts` to `positions`, to align with the attribute name and the more generic design (positions are not vertices, they are just an attribute stored on the point domain). This change is made possible by previous commits that moved all other data out of `MVert` to runtime data or other generic attributes. What remains is mostly a simple type change. Though, the type still shows up 859 times, so the patch is quite large. One compromise is that now `CD_MASK_BAREMESH` now contains `CD_PROP_FLOAT3`. With the general move towards generic attributes over custom data types, we are removing use of these type masks anyway. **Benefits** The most obvious benefit is reduced memory usage and the benefits that brings in memory-bound situations. `float3` is only 3 bytes, in comparison to `MVert` which was 4. When there are millions of vertices this starts to matter more. The other benefits come from using a more generic type. Instead of writing algorithms specifically for `MVert`, code can just use arrays of vectors. This will allow eliminating many temporary arrays or wrappers used to extract positions. Many possible improvements aren't implemented in this patch, though I did switch simplify or remove the process of creating temporary position arrays in a few places. The design clarity that "positions are just another attribute" brings allows removing explicit copying of vertices in some procedural operations-- they are just processed like most other attributes. **Performance** This touches so many areas that it's hard to benchmark exhaustively, but I observed some areas as examples. * The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster. * The Spring splash screen went from ~4.3 to ~4.5 fps. * The subdivision surface modifier/node was slightly faster RNA access through Python may be slightly slower, since now we need a name lookup instead of just a custom data type lookup for each index. **Future Improvements** * Remove uses of "vert_coords" functions: * `BKE_mesh_vert_coords_alloc` * `BKE_mesh_vert_coords_get` * `BKE_mesh_vert_coords_apply{_with_mat4}` * Remove more hidden copying of positions * General simplification now possible in many areas * Convert more code to C++ to use `float3` instead of `float[3]` * Currently `reinterpret_cast` is used for those C-API functions Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
copy_v3_v3(vcos[a], positions[a]);
2002-10-12 11:37:38 +00:00
}
return vcos;
}
void BKE_mesh_orco_verts_transform(Mesh *mesh, float (*orco)[3], int totvert, 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 (int a = 0; a < totvert; a++) {
float *co = orco[a];
madd_v3_v3v3v3(co, texspace_location, co, texspace_size);
}
}
else {
for (int a = 0; a < totvert; a++) {
float *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];
}
}
2002-10-12 11:37:38 +00:00
}
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. */
float(*orcodata)[3] = BKE_mesh_orco_verts_get(ob);
BKE_mesh_orco_verts_transform(mesh, orcodata, mesh->verts_num, false);
CustomData_add_layer_with_data(&mesh->vert_data, CD_ORCO, orcodata, mesh->verts_num, nullptr);
}
Mesh *BKE_mesh_from_object(Object *ob)
2002-10-12 11:37:38 +00:00
{
if (ob == nullptr) {
return nullptr;
}
if (ob->type == OB_MESH) {
return static_cast<Mesh *>(ob->data);
}
return nullptr;
2002-10-12 11:37:38 +00:00
}
void BKE_mesh_assign_object(Main *bmain, Object *ob, Mesh *mesh)
2002-10-12 11:37:38 +00:00
{
Mesh *old = nullptr;
if (ob == nullptr) {
return;
}
2018-06-17 17:05:51 +02:00
multires_force_sculpt_rebuild(ob);
if (ob->type == OB_MESH) {
old = static_cast<Mesh *>(ob->data);
if (old) {
id_us_min(&old->id);
}
ob->data = mesh;
id_us_plus((ID *)mesh);
2002-10-12 11:37:38 +00:00
}
2018-06-17 17:05:51 +02:00
BKE_object_materials_test(bmain, ob, (ID *)mesh);
BKE_modifiers_test_object(ob);
2002-10-12 11:37:38 +00:00
}
void BKE_mesh_material_index_remove(Mesh *mesh, short index)
{
using namespace blender;
using namespace blender::bke;
MutableAttributeAccessor attributes = mesh->attributes_for_write();
AttributeWriter<int> material_indices = attributes.lookup_for_write<int>("material_index");
if (!material_indices) {
return;
}
if (material_indices.domain != AttrDomain::Face) {
BLI_assert_unreachable();
return;
}
MutableVArraySpan<int> 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;
using namespace blender::bke;
const AttributeAccessor attributes = mesh->attributes();
const VArray<int> material_indices = *attributes.lookup_or_default<int>(
"material_index", AttrDomain::Face, 0);
if (material_indices.is_single()) {
return material_indices.get_internal_single() == index;
}
const VArraySpan<int> indices_span(material_indices);
return indices_span.contains(index);
}
void BKE_mesh_material_index_clear(Mesh *mesh)
{
using namespace blender;
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)
2015-04-28 07:24:56 +10:00
{
using namespace blender;
using namespace blender::bke;
const short remap_len_short = short(remap_len);
2015-04-28 07:24:56 +10:00
#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 (mesh->edit_mesh) {
BMEditMesh *em = mesh->edit_mesh;
2015-04-28 07:24:56 +10:00
BMIter iter;
BMFace *efa;
2015-04-28 07:24:56 +10:00
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<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
"material_index", AttrDomain::Face);
if (!material_indices) {
return;
}
for (const int i : material_indices.span.index_range()) {
MAT_NR_REMAP(material_indices.span[i]);
2015-04-28 07:24:56 +10:00
}
material_indices.span.save();
material_indices.finish();
2015-04-28 07:24:56 +10:00
}
#undef MAT_NR_REMAP
}
namespace blender::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) {
Mesh: Replace auto smooth with node group Design task: #93551 This PR replaces the auto smooth option with a geometry nodes modifier that sets the sharp edge attribute. This solves a fair number of long- standing problems related to auto smooth, simplifies the process of normal computation, and allows Blender to automatically choose between face, vertex, and face corner normals based on the sharp edge and face attributes. Versioning adds a geometry node group to objects with meshes that had auto-smooth enabled. The modifier can be applied, which also improves performance. Auto smooth is now unnecessary to get a combination of sharp and smooth edges. In general workflows are changed a bit. Separate procedural and destructive workflows are available. Custom normals can be used immediately without turning on the removed auto smooth option. **Procedural** The node group asset "Smooth by Angle" is the main way to set sharp normals based on the edge angle. It can be accessed directly in the add modifier menu. Of course the modifier can be reordered, muted, or applied like any other, or changed internally like any geometry nodes modifier. **Destructive** Often the sharp edges don't need to be dynamic. This can give better performance since edge angles don't need to be recalculated. In edit mode the two operators "Select Sharp Edges" and "Mark Sharp" can be used. In other modes, the "Shade Smooth by Angle" controls the edge sharpness directly. ### Breaking API Changes - `use_auto_smooth` is removed. Face corner normals are now used automatically if there are mixed smooth vs. not smooth tags. Meshes now always use custom normals if they exist. - In Cycles, the lack of the separate auto smooth state makes normals look triangulated when all faces are shaded smooth. - `auto_smooth_angle` is removed. Replaced by a modifier (or operator) controlling the sharp edge attribute. This means the mesh itself (without an object) doesn't know anything about automatically smoothing by angle anymore. - `create_normals_split`, `calc_normals_split`, and `free_normals_split` are removed, and are replaced by the simpler `Mesh.corner_normals` collection property. Since it gives access to the normals cache, it is automatically updated when relevant data changes. Addons are updated here: https://projects.blender.org/blender/blender-addons/pulls/104609 ### Tests - `geo_node_curves_test_deform_curves_on_surface` has slightly different results because face corner normals are used instead of interpolated vertex normals. - `bf_wavefront_obj_tests` has different export results for one file which mixed sharp and smooth faces without turning on auto smooth. - `cycles_mesh_cpu` has one object which is completely flat shaded. Previously every edge was split before rendering, now it looks triangulated. Pull Request: https://projects.blender.org/blender/blender/pulls/108014
2023-10-20 16:54:08 +02:00
attributes.remove("sharp_edge");
}
attributes.remove("sharp_face");
if (!use_smooth) {
attributes.add<bool>("sharp_face",
AttrDomain::Face,
AttributeInitVArray(VArray<bool>::ForSingle(true, mesh.faces_num)));
}
}
void mesh_sharp_edges_set_from_angle(Mesh &mesh, const float angle, const bool keep_sharp_edges)
{
MutableAttributeAccessor attributes = mesh.attributes_for_write();
Mesh: Replace auto smooth with node group Design task: #93551 This PR replaces the auto smooth option with a geometry nodes modifier that sets the sharp edge attribute. This solves a fair number of long- standing problems related to auto smooth, simplifies the process of normal computation, and allows Blender to automatically choose between face, vertex, and face corner normals based on the sharp edge and face attributes. Versioning adds a geometry node group to objects with meshes that had auto-smooth enabled. The modifier can be applied, which also improves performance. Auto smooth is now unnecessary to get a combination of sharp and smooth edges. In general workflows are changed a bit. Separate procedural and destructive workflows are available. Custom normals can be used immediately without turning on the removed auto smooth option. **Procedural** The node group asset "Smooth by Angle" is the main way to set sharp normals based on the edge angle. It can be accessed directly in the add modifier menu. Of course the modifier can be reordered, muted, or applied like any other, or changed internally like any geometry nodes modifier. **Destructive** Often the sharp edges don't need to be dynamic. This can give better performance since edge angles don't need to be recalculated. In edit mode the two operators "Select Sharp Edges" and "Mark Sharp" can be used. In other modes, the "Shade Smooth by Angle" controls the edge sharpness directly. ### Breaking API Changes - `use_auto_smooth` is removed. Face corner normals are now used automatically if there are mixed smooth vs. not smooth tags. Meshes now always use custom normals if they exist. - In Cycles, the lack of the separate auto smooth state makes normals look triangulated when all faces are shaded smooth. - `auto_smooth_angle` is removed. Replaced by a modifier (or operator) controlling the sharp edge attribute. This means the mesh itself (without an object) doesn't know anything about automatically smoothing by angle anymore. - `create_normals_split`, `calc_normals_split`, and `free_normals_split` are removed, and are replaced by the simpler `Mesh.corner_normals` collection property. Since it gives access to the normals cache, it is automatically updated when relevant data changes. Addons are updated here: https://projects.blender.org/blender/blender-addons/pulls/104609 ### Tests - `geo_node_curves_test_deform_curves_on_surface` has slightly different results because face corner normals are used instead of interpolated vertex normals. - `bf_wavefront_obj_tests` has different export results for one file which mixed sharp and smooth faces without turning on auto smooth. - `cycles_mesh_cpu` has one object which is completely flat shaded. Previously every edge was split before rendering, now it looks triangulated. Pull Request: https://projects.blender.org/blender/blender/pulls/108014
2023-10-20 16:54:08 +02:00
if (angle >= M_PI) {
mesh_smooth_set(mesh, true, keep_sharp_edges);
Mesh: Replace auto smooth with node group Design task: #93551 This PR replaces the auto smooth option with a geometry nodes modifier that sets the sharp edge attribute. This solves a fair number of long- standing problems related to auto smooth, simplifies the process of normal computation, and allows Blender to automatically choose between face, vertex, and face corner normals based on the sharp edge and face attributes. Versioning adds a geometry node group to objects with meshes that had auto-smooth enabled. The modifier can be applied, which also improves performance. Auto smooth is now unnecessary to get a combination of sharp and smooth edges. In general workflows are changed a bit. Separate procedural and destructive workflows are available. Custom normals can be used immediately without turning on the removed auto smooth option. **Procedural** The node group asset "Smooth by Angle" is the main way to set sharp normals based on the edge angle. It can be accessed directly in the add modifier menu. Of course the modifier can be reordered, muted, or applied like any other, or changed internally like any geometry nodes modifier. **Destructive** Often the sharp edges don't need to be dynamic. This can give better performance since edge angles don't need to be recalculated. In edit mode the two operators "Select Sharp Edges" and "Mark Sharp" can be used. In other modes, the "Shade Smooth by Angle" controls the edge sharpness directly. ### Breaking API Changes - `use_auto_smooth` is removed. Face corner normals are now used automatically if there are mixed smooth vs. not smooth tags. Meshes now always use custom normals if they exist. - In Cycles, the lack of the separate auto smooth state makes normals look triangulated when all faces are shaded smooth. - `auto_smooth_angle` is removed. Replaced by a modifier (or operator) controlling the sharp edge attribute. This means the mesh itself (without an object) doesn't know anything about automatically smoothing by angle anymore. - `create_normals_split`, `calc_normals_split`, and `free_normals_split` are removed, and are replaced by the simpler `Mesh.corner_normals` collection property. Since it gives access to the normals cache, it is automatically updated when relevant data changes. Addons are updated here: https://projects.blender.org/blender/blender-addons/pulls/104609 ### Tests - `geo_node_curves_test_deform_curves_on_surface` has slightly different results because face corner normals are used instead of interpolated vertex normals. - `bf_wavefront_obj_tests` has different export results for one file which mixed sharp and smooth faces without turning on auto smooth. - `cycles_mesh_cpu` has one object which is completely flat shaded. Previously every edge was split before rendering, now it looks triangulated. Pull Request: https://projects.blender.org/blender/blender/pulls/108014
2023-10-20 16:54:08 +02:00
return;
}
Mesh: Replace auto smooth with node group Design task: #93551 This PR replaces the auto smooth option with a geometry nodes modifier that sets the sharp edge attribute. This solves a fair number of long- standing problems related to auto smooth, simplifies the process of normal computation, and allows Blender to automatically choose between face, vertex, and face corner normals based on the sharp edge and face attributes. Versioning adds a geometry node group to objects with meshes that had auto-smooth enabled. The modifier can be applied, which also improves performance. Auto smooth is now unnecessary to get a combination of sharp and smooth edges. In general workflows are changed a bit. Separate procedural and destructive workflows are available. Custom normals can be used immediately without turning on the removed auto smooth option. **Procedural** The node group asset "Smooth by Angle" is the main way to set sharp normals based on the edge angle. It can be accessed directly in the add modifier menu. Of course the modifier can be reordered, muted, or applied like any other, or changed internally like any geometry nodes modifier. **Destructive** Often the sharp edges don't need to be dynamic. This can give better performance since edge angles don't need to be recalculated. In edit mode the two operators "Select Sharp Edges" and "Mark Sharp" can be used. In other modes, the "Shade Smooth by Angle" controls the edge sharpness directly. ### Breaking API Changes - `use_auto_smooth` is removed. Face corner normals are now used automatically if there are mixed smooth vs. not smooth tags. Meshes now always use custom normals if they exist. - In Cycles, the lack of the separate auto smooth state makes normals look triangulated when all faces are shaded smooth. - `auto_smooth_angle` is removed. Replaced by a modifier (or operator) controlling the sharp edge attribute. This means the mesh itself (without an object) doesn't know anything about automatically smoothing by angle anymore. - `create_normals_split`, `calc_normals_split`, and `free_normals_split` are removed, and are replaced by the simpler `Mesh.corner_normals` collection property. Since it gives access to the normals cache, it is automatically updated when relevant data changes. Addons are updated here: https://projects.blender.org/blender/blender-addons/pulls/104609 ### Tests - `geo_node_curves_test_deform_curves_on_surface` has slightly different results because face corner normals are used instead of interpolated vertex normals. - `bf_wavefront_obj_tests` has different export results for one file which mixed sharp and smooth faces without turning on auto smooth. - `cycles_mesh_cpu` has one object which is completely flat shaded. Previously every edge was split before rendering, now it looks triangulated. Pull Request: https://projects.blender.org/blender/blender/pulls/108014
2023-10-20 16:54:08 +02:00
if (angle == 0.0f) {
mesh_smooth_set(mesh, false, keep_sharp_edges);
Mesh: Replace auto smooth with node group Design task: #93551 This PR replaces the auto smooth option with a geometry nodes modifier that sets the sharp edge attribute. This solves a fair number of long- standing problems related to auto smooth, simplifies the process of normal computation, and allows Blender to automatically choose between face, vertex, and face corner normals based on the sharp edge and face attributes. Versioning adds a geometry node group to objects with meshes that had auto-smooth enabled. The modifier can be applied, which also improves performance. Auto smooth is now unnecessary to get a combination of sharp and smooth edges. In general workflows are changed a bit. Separate procedural and destructive workflows are available. Custom normals can be used immediately without turning on the removed auto smooth option. **Procedural** The node group asset "Smooth by Angle" is the main way to set sharp normals based on the edge angle. It can be accessed directly in the add modifier menu. Of course the modifier can be reordered, muted, or applied like any other, or changed internally like any geometry nodes modifier. **Destructive** Often the sharp edges don't need to be dynamic. This can give better performance since edge angles don't need to be recalculated. In edit mode the two operators "Select Sharp Edges" and "Mark Sharp" can be used. In other modes, the "Shade Smooth by Angle" controls the edge sharpness directly. ### Breaking API Changes - `use_auto_smooth` is removed. Face corner normals are now used automatically if there are mixed smooth vs. not smooth tags. Meshes now always use custom normals if they exist. - In Cycles, the lack of the separate auto smooth state makes normals look triangulated when all faces are shaded smooth. - `auto_smooth_angle` is removed. Replaced by a modifier (or operator) controlling the sharp edge attribute. This means the mesh itself (without an object) doesn't know anything about automatically smoothing by angle anymore. - `create_normals_split`, `calc_normals_split`, and `free_normals_split` are removed, and are replaced by the simpler `Mesh.corner_normals` collection property. Since it gives access to the normals cache, it is automatically updated when relevant data changes. Addons are updated here: https://projects.blender.org/blender/blender-addons/pulls/104609 ### Tests - `geo_node_curves_test_deform_curves_on_surface` has slightly different results because face corner normals are used instead of interpolated vertex normals. - `bf_wavefront_obj_tests` has different export results for one file which mixed sharp and smooth faces without turning on auto smooth. - `cycles_mesh_cpu` has one object which is completely flat shaded. Previously every edge was split before rendering, now it looks triangulated. Pull Request: https://projects.blender.org/blender/blender/pulls/108014
2023-10-20 16:54:08 +02:00
return;
}
if (!keep_sharp_edges) {
attributes.remove("sharp_edge");
}
SpanAttributeWriter<bool> sharp_edges = attributes.lookup_or_add_for_write_span<bool>(
"sharp_edge", AttrDomain::Edge);
const VArraySpan<bool> sharp_faces = *attributes.lookup<bool>("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);
Mesh: Replace auto smooth with node group Design task: #93551 This PR replaces the auto smooth option with a geometry nodes modifier that sets the sharp edge attribute. This solves a fair number of long- standing problems related to auto smooth, simplifies the process of normal computation, and allows Blender to automatically choose between face, vertex, and face corner normals based on the sharp edge and face attributes. Versioning adds a geometry node group to objects with meshes that had auto-smooth enabled. The modifier can be applied, which also improves performance. Auto smooth is now unnecessary to get a combination of sharp and smooth edges. In general workflows are changed a bit. Separate procedural and destructive workflows are available. Custom normals can be used immediately without turning on the removed auto smooth option. **Procedural** The node group asset "Smooth by Angle" is the main way to set sharp normals based on the edge angle. It can be accessed directly in the add modifier menu. Of course the modifier can be reordered, muted, or applied like any other, or changed internally like any geometry nodes modifier. **Destructive** Often the sharp edges don't need to be dynamic. This can give better performance since edge angles don't need to be recalculated. In edit mode the two operators "Select Sharp Edges" and "Mark Sharp" can be used. In other modes, the "Shade Smooth by Angle" controls the edge sharpness directly. ### Breaking API Changes - `use_auto_smooth` is removed. Face corner normals are now used automatically if there are mixed smooth vs. not smooth tags. Meshes now always use custom normals if they exist. - In Cycles, the lack of the separate auto smooth state makes normals look triangulated when all faces are shaded smooth. - `auto_smooth_angle` is removed. Replaced by a modifier (or operator) controlling the sharp edge attribute. This means the mesh itself (without an object) doesn't know anything about automatically smoothing by angle anymore. - `create_normals_split`, `calc_normals_split`, and `free_normals_split` are removed, and are replaced by the simpler `Mesh.corner_normals` collection property. Since it gives access to the normals cache, it is automatically updated when relevant data changes. Addons are updated here: https://projects.blender.org/blender/blender-addons/pulls/104609 ### Tests - `geo_node_curves_test_deform_curves_on_surface` has slightly different results because face corner normals are used instead of interpolated vertex normals. - `bf_wavefront_obj_tests` has different export results for one file which mixed sharp and smooth faces without turning on auto smooth. - `cycles_mesh_cpu` has one object which is completely flat shaded. Previously every edge was split before rendering, now it looks triangulated. Pull Request: https://projects.blender.org/blender/blender/pulls/108014
2023-10-20 16:54:08 +02:00
sharp_edges.finish();
}
} // namespace blender::bke
std::optional<blender::Bounds<blender::float3>> Mesh::bounds_min_max() const
{
using namespace blender;
const int verts_num = BKE_mesh_wrapper_vert_len(this);
if (verts_num == 0) {
return std::nullopt;
}
this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
switch (this->runtime->wrapper_type) {
case ME_WRAPPER_TYPE_BMESH:
r_bounds = *BKE_editmesh_cache_calc_minmax(*this->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 blender::Bounds<float3> &bounds)
{
this->runtime->bounds_cache.ensure([&](blender::Bounds<float3> &r_data) { r_data = bounds; });
}
void BKE_mesh_transform(Mesh *mesh, const float mat[4][4], bool do_keys)
{
MutableSpan<float3> positions = mesh->vert_positions_for_write();
Mesh: Move positions to a generic attribute **Changes** As described in T93602, this patch removes all use of the `MVert` struct, replacing it with a generic named attribute with the name `"position"`, consistent with other geometry types. Variable names have been changed from `verts` to `positions`, to align with the attribute name and the more generic design (positions are not vertices, they are just an attribute stored on the point domain). This change is made possible by previous commits that moved all other data out of `MVert` to runtime data or other generic attributes. What remains is mostly a simple type change. Though, the type still shows up 859 times, so the patch is quite large. One compromise is that now `CD_MASK_BAREMESH` now contains `CD_PROP_FLOAT3`. With the general move towards generic attributes over custom data types, we are removing use of these type masks anyway. **Benefits** The most obvious benefit is reduced memory usage and the benefits that brings in memory-bound situations. `float3` is only 3 bytes, in comparison to `MVert` which was 4. When there are millions of vertices this starts to matter more. The other benefits come from using a more generic type. Instead of writing algorithms specifically for `MVert`, code can just use arrays of vectors. This will allow eliminating many temporary arrays or wrappers used to extract positions. Many possible improvements aren't implemented in this patch, though I did switch simplify or remove the process of creating temporary position arrays in a few places. The design clarity that "positions are just another attribute" brings allows removing explicit copying of vertices in some procedural operations-- they are just processed like most other attributes. **Performance** This touches so many areas that it's hard to benchmark exhaustively, but I observed some areas as examples. * The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster. * The Spring splash screen went from ~4.3 to ~4.5 fps. * The subdivision surface modifier/node was slightly faster RNA access through Python may be slightly slower, since now we need a name lookup instead of just a custom data type lookup for each index. **Future Improvements** * Remove uses of "vert_coords" functions: * `BKE_mesh_vert_coords_alloc` * `BKE_mesh_vert_coords_get` * `BKE_mesh_vert_coords_apply{_with_mat4}` * Remove more hidden copying of positions * General simplification now possible in many areas * Convert more code to C++ to use `float3` instead of `float[3]` * Currently `reinterpret_cast` is used for those C-API functions Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
for (float3 &position : positions) {
mul_m4_v3(mat, position);
}
if (do_keys && mesh->key) {
LISTBASE_FOREACH (KeyBlock *, kb, &mesh->key->block) {
float *fp = (float *)kb->data;
Mesh: Remove redundant custom data pointers For copy-on-write, we want to share attribute arrays between meshes where possible. Mutable pointers like `Mesh.mvert` make that difficult by making ownership vague. They also make code more complex by adding redundancy. The simplest solution is just removing them and retrieving layers from `CustomData` as needed. Similar changes have already been applied to curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of the pointers generally makes code more obvious and more reusable. Mesh data is now accessed with a C++ API (`Mesh::edges()` or `Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`). The CoW changes this commit makes possible are described in T95845 and T95842, and started in D14139 and D14140. The change also simplifies the ongoing mesh struct-of-array refactors from T95965. **RNA/Python Access Performance** Theoretically, accessing mesh elements with the RNA API may become slower, since the layer needs to be found on every random access. However, overhead is already high enough that this doesn't make a noticible differenc, and performance is actually improved in some cases. Random access can be up to 10% faster, but other situations might be a bit slower. Generally using `foreach_get/set` are the best way to improve performance. See the differential revision for more discussion about Python performance. Cycles has been updated to use raw pointers and the internal Blender mesh types, mostly because there is no sense in having this overhead when it's already compiled with Blender. In my tests this roughly halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million face grid). Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
for (int i = kb->totelem; i--; fp += 3) {
mul_m4_v3(mat, fp);
}
}
}
mesh->tag_positions_changed();
}
static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
{
using namespace blender;
threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
for (float3 &position : positions.slice(range)) {
position += translation;
}
});
}
void BKE_mesh_translate(Mesh *mesh, const float offset[3], const bool do_keys)
{
using namespace blender;
if (math::is_zero(float3(offset))) {
return;
}
2018-06-17 17:05:51 +02:00
std::optional<Bounds<float3>> bounds;
if (mesh->runtime->bounds_cache.is_cached()) {
bounds = mesh->runtime->bounds_cache.data();
}
translate_positions(mesh->vert_positions_for_write(), offset);
if (do_keys && mesh->key) {
LISTBASE_FOREACH (KeyBlock *, kb, &mesh->key->block) {
translate_positions({static_cast<float3 *>(kb->data), kb->totelem}, offset);
}
}
mesh->tag_positions_changed_uniformly();
if (bounds) {
bounds->min += offset;
bounds->max += offset;
mesh->bounds_set_eager(*bounds);
}
}
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_FREE(mesh->mselect);
mesh->totselect = 0;
}
void BKE_mesh_mselect_validate(Mesh *mesh)
{
2022-09-23 09:38:37 -05:00
using namespace blender;
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 = (MSelect *)MEM_malloc_arrayN(
(mesh->totselect), sizeof(MSelect), "Mesh selection history");
const AttributeAccessor attributes = mesh->attributes();
const VArray<bool> select_vert = *attributes.lookup_or_default<bool>(
".select_vert", AttrDomain::Point, false);
const VArray<bool> select_edge = *attributes.lookup_or_default<bool>(
".select_edge", AttrDomain::Edge, false);
const VArray<bool> select_poly = *attributes.lookup_or_default<bool>(
".select_poly", AttrDomain::Face, false);
2022-09-23 09:38:37 -05:00
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: {
2022-09-23 09:38:37 -05:00
if (select_vert[index]) {
mselect_dst[i_dst] = mselect_src[i_src];
i_dst++;
}
break;
}
case ME_ESEL: {
2022-09-23 09:38:37 -05:00
if (select_edge[index]) {
mselect_dst[i_dst] = mselect_src[i_src];
i_dst++;
}
break;
}
case ME_FSEL: {
2022-09-23 09:38:37 -05:00
if (select_poly[index]) {
mselect_dst[i_dst] = mselect_src[i_src];
i_dst++;
}
break;
}
default: {
BLI_assert_unreachable();
break;
}
}
}
MEM_freeN(mselect_src);
if (i_dst == 0) {
MEM_freeN(mselect_dst);
mselect_dst = nullptr;
}
else if (i_dst != mesh->totselect) {
mselect_dst = (MSelect *)MEM_reallocN(mselect_dst, sizeof(MSelect) * i_dst);
}
mesh->totselect = i_dst;
mesh->mselect = mselect_dst;
}
int BKE_mesh_mselect_find(Mesh *mesh, int index, int 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(Mesh *mesh, int 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, int type)
{
const int msel_index = BKE_mesh_mselect_find(mesh, index, type);
if (msel_index == -1) {
/* add to the end */
mesh->mselect = (MSelect *)MEM_reallocN(mesh->mselect,
sizeof(MSelect) * (mesh->totselect + 1));
mesh->mselect[mesh->totselect].index = index;
mesh->mselect[mesh->totselect].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));
}
Bake API - bpy.ops.object.bake() New operator that can calls a bake function to the current render engine when available. This commit provides no feature for the users, but allows external engines to be accessed by the operator and be integrated with the baking api. The API itself is simple. Blender sends a populated array of BakePixels to the renderer, and gets back an array of floats with the result. The Blender Internal (and multires) system is still running independent, but we eventually will pipe it through the API as well. Cycles baking will come next as a separated commit Python Operator: ---------------- The operator can be called with some arguments, or a user interface can be created for it. In that case the arguments can be ommited and the interface can expose the settings from bpy.context.scene.render.bake bpy.ops.object.bake(type='COMBINED', filepath="", width=512, height=512, margin=16, use_selected_to_active=False, cage_extrusion=0, cage="", normal_space='TANGENT', normal_r='POS_X', normal_g='POS_Y', normal_b='POS_Z', save_mode='INTERNAL', use_clear=False, use_split_materials=False, use_automatic_name=False) Note: external save mode is currently disabled. Supported Features: ------------------ * Margin - Baked result is extended this many pixels beyond the border of each UV "island," to soften seams in the texture. * Selected to Active - bake shading on the surface of selected object to the active object. The rays are cast from the lowpoly object inwards towards the highpoly object. If the highpoly object is not entirely involved by the lowpoly object, you can tweak the rays start point with Cage Extrusion. For even more control of the cage you can use a Cage object. * Cage Extrusion - distance to use for the inward ray cast when using selected to active * Custom Cage - object to use as cage (instead of the lowpoly object). * Normal swizzle - change the axis that gets mapped to RGB * Normal space - save as tangent or object normal spaces Supported Passes: ----------------- Any pass that is supported by Blender renderlayer system. Though it's up to the external engine to provide a valid enum with its supported passes. Normal passes get a special treatment since we post-process them to converted and "swizzled" Development Notes for External Engines: --------------------------------------- (read them in bake_api.c) * For a complete implementation example look at the Cycles Bake commit (next). Review: D421 Reviewed by: Campbell Barton, Brecht van Lommel, Sergey Sharybin, Thomas Dinge Normal map pipeline "consulting" by Andy Davies (metalliandy) Original design by Brecht van Lommel. The entire commit history can be found on the branch: bake-cycles
2014-01-02 19:05:07 -02:00
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->edit_mesh) {
BMesh *bm = mesh->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 **** */
2018-07-13 08:36:10 +02:00
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) {
mesh->runtime->mesh_eval->edit_mesh = nullptr;
BKE_id_free(nullptr, mesh->runtime->mesh_eval);
mesh->runtime->mesh_eval = nullptr;
}
if (DEG_is_active(depsgraph)) {
Mesh *mesh_orig = reinterpret_cast<Mesh *>(DEG_get_original_id(&mesh->id));
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);
}
}
}