WIP: Mesh: Improve and simplify modifier evaluation logic #119968

Draft
Hans Goudey wants to merge 28 commits from HooglyBoogly/blender:fix-modifier-eval-geometry-set-improve into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
6 changed files with 287 additions and 401 deletions

View File

@ -168,6 +168,13 @@ struct GeometrySet {
return static_cast<Component &>(this->get_component_for_write(Component::static_type));
}
GeometryComponentPtr get_component_ptr(GeometryComponent::Type component_type) const;
template<typename Component> ImplicitSharingPtr<Component> get_component_ptr() const
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return static_cast<ImplicitSharingPtr<Component>>(components_(Component::static_type));
}
/**
* Get the component of the given type. Might return null if the component does not exist yet.
*/
@ -428,11 +435,11 @@ struct GeometrySet {
* Retrieve the pointer to a component without creating it if it does not exist,
* unlike #get_component_for_write.
*/
GeometryComponent *get_component_ptr(GeometryComponent::Type type);
template<typename Component> Component *get_component_ptr()
GeometryComponent *get_component_for_write_ptr(GeometryComponent::Type type);
template<typename Component> Component *get_component_for_write_ptr()
{
BLI_STATIC_ASSERT(is_geometry_component_v<Component>, "");
return static_cast<Component *>(get_component_ptr(Component::static_type));
return static_cast<Component *>(get_component_for_write_ptr(Component::static_type));
}
};
@ -675,6 +682,25 @@ class VolumeComponent : public GeometryComponent {
static constexpr inline GeometryComponent::Type static_type = Type::Volume;
};
struct MeshEditHints {
/**
* Mesh created by object evaluation. It only has leading deformation modifiers applied.
*
* \todo This should use a similar system to #CurvesEditHints storing just an array for the
* deformed positions, but for historical reasons we copy the whole mesh.
*/
ImplicitSharingPtr<MeshComponent> mesh_deform;
/**
* Evaluated mesh cage in edit mode.
*
* \note When the mesh's `runtime->deformed_only` is true, its vertex positions and other
* geometry arrays will be aligned the edit-mesh. Otherwise the #CD_ORIGINDEX custom-data should
* be used to map the cage geometry back to the original indices, see
* #eModifierTypeFlag_SupportsMapping.
*/
ImplicitSharingPtr<MeshComponent> mesh_cage;
};
/**
* When the original data is in some edit mode, we want to propagate some additional information
* through object evaluation. This information can be used by edit modes to support working on
@ -696,6 +722,8 @@ class GeometryComponentEditData final : public GeometryComponent {
*/
std::unique_ptr<GreasePencilEditHints> grease_pencil_edit_hints_;
std::unique_ptr<MeshEditHints> mesh_edit_hints_;
GeometryComponentEditData();
GeometryComponentPtr copy() const final;

View File

@ -84,22 +84,6 @@ struct ObjectRuntime {
*/
GeometrySet *geometry_set_eval = nullptr;
/**
* Mesh structure created during object evaluation.
* It has deformation only modifiers applied on it.
*/
Mesh *mesh_deform_eval = nullptr;
/**
* Evaluated mesh cage in edit mode.
*
* \note When the mesh's `runtime->deformed_only` is true, the meshes vertex positions
* and other geometry arrays will be aligned the edit-mesh. Otherwise the #CD_ORIGINDEX
* custom-data should be used to map the cage geometry back to the original indices, see
* #eModifierTypeFlag_SupportsMapping.
*/
Mesh *editmesh_eval_cage = nullptr;
/**
* Original grease pencil bGPdata pointer, before object->data was changed to point
* to gpd_eval.

View File

@ -133,7 +133,7 @@ GeometryComponent &GeometrySet::get_component_for_write(GeometryComponent::Type
return const_cast<GeometryComponent &>(*component_ptr);
}
GeometryComponent *GeometrySet::get_component_ptr(GeometryComponent::Type type)
GeometryComponent *GeometrySet::get_component_for_write_ptr(GeometryComponent::Type type)
{
if (this->has(type)) {
return &this->get_component_for_write(type);
@ -529,31 +529,31 @@ void GeometrySet::replace_grease_pencil(GreasePencil *grease_pencil,
Mesh *GeometrySet::get_mesh_for_write()
{
MeshComponent *component = this->get_component_ptr<MeshComponent>();
MeshComponent *component = this->get_component_for_write_ptr<MeshComponent>();
return component == nullptr ? nullptr : component->get_for_write();
}
PointCloud *GeometrySet::get_pointcloud_for_write()
{
PointCloudComponent *component = this->get_component_ptr<PointCloudComponent>();
PointCloudComponent *component = this->get_component_for_write_ptr<PointCloudComponent>();
return component == nullptr ? nullptr : component->get_for_write();
}
Volume *GeometrySet::get_volume_for_write()
{
VolumeComponent *component = this->get_component_ptr<VolumeComponent>();
VolumeComponent *component = this->get_component_for_write_ptr<VolumeComponent>();
return component == nullptr ? nullptr : component->get_for_write();
}
Curves *GeometrySet::get_curves_for_write()
{
CurveComponent *component = this->get_component_ptr<CurveComponent>();
CurveComponent *component = this->get_component_for_write_ptr<CurveComponent>();
return component == nullptr ? nullptr : component->get_for_write();
}
Instances *GeometrySet::get_instances_for_write()
{
InstancesComponent *component = this->get_component_ptr<InstancesComponent>();
InstancesComponent *component = this->get_component_for_write_ptr<InstancesComponent>();
return component == nullptr ? nullptr : component->get_for_write();
}
@ -569,7 +569,7 @@ CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write()
GreasePencil *GeometrySet::get_grease_pencil_for_write()
{
GreasePencilComponent *component = this->get_component_ptr<GreasePencilComponent>();
GreasePencilComponent *component = this->get_component_for_write_ptr<GreasePencilComponent>();
return component == nullptr ? nullptr : component->get_for_write();
}

View File

@ -240,50 +240,26 @@ static void mesh_calc_finalize(const Mesh &mesh_input, Mesh &mesh_eval)
* The purpose of the geometry set is to store all geometry components that are generated
* by modifiers to allow outputting non-mesh data from modifiers.
*/
static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md,
const ModifierEvalContext &mectx,
Mesh *input_mesh,
GeometrySet &geometry_set)
static void modifier_modify_mesh_and_geometry_set(ModifierData *md,
const ModifierEvalContext &mectx,
GeometrySet &geometry_set)
{
Mesh *mesh_output = nullptr;
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (mti->modify_geometry_set == nullptr) {
mesh_output = BKE_modifier_modify_mesh(md, &mectx, input_mesh);
if (mti->modify_geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
BKE_mesh_wrapper_ensure_mdata(mesh);
}
mti->modify_geometry_set(md, &mectx, &geometry_set);
}
else {
/* For performance reasons, this should be called by the modifier and/or nodes themselves at
* some point. */
BKE_mesh_wrapper_ensure_mdata(input_mesh);
/* Replace only the mesh rather than the whole component, because the entire #MeshComponent
* might have been replaced by data from a different object in the node tree, which means the
* component contains vertex group name data for that object that should not be removed. */
geometry_set.replace_mesh(input_mesh, GeometryOwnershipType::Editable);
/* Let the modifier change the geometry set. */
mti->modify_geometry_set(md, &mectx, &geometry_set);
/* Release the mesh from the geometry set again. */
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
if (mesh_component.get() != input_mesh) {
/* Make sure the mesh component actually owns the mesh before taking over ownership. */
mesh_component.ensure_owns_direct_data();
}
mesh_output = mesh_component.release();
}
/* Need to ensure that non-mesh data is also owned by the geometry set. Otherwise it might be
* freed while there is still a reference to it in the geometry. */
geometry_set.ensure_owns_direct_data();
/* Return an empty mesh instead of null. */
if (mesh_output == nullptr) {
mesh_output = BKE_mesh_new_nomain(0, 0, 0, 0);
BKE_mesh_copy_parameters_for_eval(mesh_output, input_mesh);
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
geometry_set.replace_mesh(BKE_modifier_modify_mesh(md, &mectx, mesh));
}
}
return mesh_output;
if (!geometry_set.has_mesh()) {
geometry_set.replace_mesh(BKE_mesh_new_nomain(0, 0, 0, 0));
}
}
static void set_rest_position(Mesh &mesh)
@ -305,30 +281,36 @@ static void set_rest_position(Mesh &mesh)
}
}
static void mesh_calc_modifiers(Depsgraph &depsgraph,
const Scene &scene,
Object &ob,
const bool use_deform,
const bool need_mapping,
const CustomData_MeshMasks &dataMask,
const bool use_cache,
const bool allow_shared_mesh,
/* return args */
Mesh **r_deform,
Mesh **r_final,
GeometrySet **r_geometry_set)
static MeshEditHints &geometry_mesh_edit_hints_ensure(GeometrySet &geometry)
{
/* Input mesh shouldn't be modified. */
Mesh &mesh_input = *static_cast<Mesh *>(ob.data);
/* The final mesh is the result of calculating all enabled modifiers. */
Mesh *mesh = nullptr;
/* The result of calculating all leading deform modifiers. */
Mesh *mesh_deform = nullptr;
/* This geometry set contains the non-mesh data that might be generated by modifiers. */
GeometrySet geometry_set_final;
auto &edit_data = geometry.get_component_for_write<GeometryComponentEditData>();
if (!edit_data.mesh_edit_hints_) {
edit_data.mesh_edit_hints_ = std::make_unique<MeshEditHints>();
}
return *edit_data.mesh_edit_hints_;
}
static void save_deform_mesh(GeometrySet &geometry)
{
MeshEditHints &edit_data = geometry_mesh_edit_hints_ensure(geometry);
edit_data.mesh_deform = geometry.get_component_ptr<MeshComponent>();
}
static GeometrySet mesh_calc_modifiers(Depsgraph &depsgraph,
const Scene &scene,
Object &ob,
const bool use_deform,
const bool need_mapping,
const CustomData_MeshMasks &dataMask,
const bool use_cache)
{
const Mesh &mesh_input = *static_cast<const Mesh *>(ob.data);
BLI_assert((mesh_input.id.tag & LIB_TAG_COPIED_ON_EVAL_FINAL_RESULT) == 0);
GeometrySet geometry_set = GeometrySet::from_mesh(const_cast<Mesh *>(&mesh_input),
GeometryOwnershipType::ReadOnly);
/* Mesh with constructive modifiers but no deformation applied. Tracked
* along with final mesh if undeformed / orco coordinates are requested
* for texturing. */
@ -371,11 +353,9 @@ static void mesh_calc_modifiers(Depsgraph &depsgraph,
BKE_modifiers_clear_errors(&ob);
if (ob.modifier_flag & OB_MODIFIER_FLAG_ADD_REST_POSITION) {
if (mesh == nullptr) {
mesh = BKE_mesh_copy_for_eval(mesh_input);
ASSERT_IS_VALID_MESH(mesh);
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
set_rest_position(*mesh);
}
set_rest_position(*mesh);
}
/* Apply all leading deform modifiers. */
@ -389,20 +369,17 @@ static void mesh_calc_modifiers(Depsgraph &depsgraph,
if (mti->type == ModifierTypeType::OnlyDeform && !sculpt_dyntopo) {
ScopedModifierTimer modifier_timer{*md};
if (!mesh) {
mesh = BKE_mesh_copy_for_eval(mesh_input);
ASSERT_IS_VALID_MESH(mesh);
}
if (mti->required_data_mask) {
CustomData_MeshMasks mask{};
mti->required_data_mask(md, &mask);
if (mask.vmask & CD_MASK_ORCO) {
add_orco_mesh(ob, nullptr, *mesh, nullptr, CD_ORCO);
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
if (mti->required_data_mask) {
CustomData_MeshMasks mask{};
mti->required_data_mask(md, &mask);
if (mask.vmask & CD_MASK_ORCO) {
add_orco_mesh(ob, nullptr, *mesh, nullptr, CD_ORCO);
}
}
}
BKE_modifier_deform_verts(md, &mectx, mesh, mesh->vert_positions_for_write());
BKE_modifier_deform_verts(md, &mectx, mesh, mesh->vert_positions_for_write());
}
}
else {
break;
@ -412,9 +389,7 @@ static void mesh_calc_modifiers(Depsgraph &depsgraph,
/* Result of all leading deforming modifiers is cached for
* places that wish to use the original mesh but with deformed
* coordinates (like vertex paint). */
if (r_deform) {
mesh_deform = BKE_mesh_copy_for_eval(mesh ? *mesh : mesh_input);
}
save_deform_mesh(geometry_set);
}
/* Apply all remaining constructive and deforming modifiers. */
@ -475,42 +450,28 @@ static void mesh_calc_modifiers(Depsgraph &depsgraph,
ScopedModifierTimer modifier_timer{*md};
/* Add orco mesh as layer if needed by this modifier. */
if (mesh && mesh_orco && mti->required_data_mask) {
CustomData_MeshMasks mask = {0};
mti->required_data_mask(md, &mask);
if (mask.vmask & CD_MASK_ORCO) {
add_orco_mesh(ob, nullptr, *mesh, mesh_orco, CD_ORCO);
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
if (mesh_orco && mti->required_data_mask) {
CustomData_MeshMasks mask = {0};
mti->required_data_mask(md, &mask);
if (mask.vmask & CD_MASK_ORCO) {
add_orco_mesh(ob, nullptr, *mesh, mesh_orco, CD_ORCO);
}
}
}
if (mti->type == ModifierTypeType::OnlyDeform) {
if (!mesh) {
mesh = BKE_mesh_copy_for_eval(mesh_input);
ASSERT_IS_VALID_MESH(mesh);
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
BKE_modifier_deform_verts(md, &mectx, mesh, mesh->vert_positions_for_write());
}
BKE_modifier_deform_verts(md, &mectx, mesh, mesh->vert_positions_for_write());
}
else {
bool check_for_needs_mapping = false;
if (mesh != nullptr) {
if (have_non_onlydeform_modifiers_applied == false) {
/* If we only deformed, we won't have initialized #CD_ORIGINDEX.
* as this is the only part of the function that initializes mapping. */
check_for_needs_mapping = true;
}
}
else {
mesh = BKE_mesh_copy_for_eval(mesh_input);
ASSERT_IS_VALID_MESH(mesh);
check_for_needs_mapping = true;
}
have_non_onlydeform_modifiers_applied = true;
/* determine which data layers are needed by following modifiers */
CustomData_MeshMasks nextmask = md_datamask->next ? md_datamask->next->mask : final_datamask;
if (check_for_needs_mapping) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
/* Initialize original indices the first time we evaluate a
* constructive modifier. Modifiers will then do mapping mostly
* automatic by copying them through CustomData_copy_data along
@ -541,45 +502,35 @@ static void mesh_calc_modifiers(Depsgraph &depsgraph,
mesh->faces_num,
0);
}
}
/* set the Mesh to only copy needed data */
CustomData_MeshMasks mask = md_datamask->mask;
/* needMapping check here fixes bug #28112, otherwise it's
* possible that it won't be copied */
CustomData_MeshMasks_update(&mask, &append_mask);
if (need_mapping) {
mask.vmask |= CD_MASK_ORIGINDEX;
mask.emask |= CD_MASK_ORIGINDEX;
mask.pmask |= CD_MASK_ORIGINDEX;
}
mesh_set_only_copy(mesh, &mask);
/* set the Mesh to only copy needed data */
CustomData_MeshMasks mask = md_datamask->mask;
/* needMapping check here fixes bug #28112, otherwise it's
* possible that it won't be copied */
CustomData_MeshMasks_update(&mask, &append_mask);
if (need_mapping) {
mask.vmask |= CD_MASK_ORIGINDEX;
mask.emask |= CD_MASK_ORIGINDEX;
mask.pmask |= CD_MASK_ORIGINDEX;
}
mesh_set_only_copy(mesh, &mask);
/* add cloth rest shape key if needed */
if (mask.vmask & CD_MASK_CLOTH_ORCO) {
add_orco_mesh(ob, nullptr, *mesh, mesh_orco, CD_CLOTH_ORCO);
}
/* add cloth rest shape key if needed */
if (mask.vmask & CD_MASK_CLOTH_ORCO) {
add_orco_mesh(ob, nullptr, *mesh, mesh_orco, CD_CLOTH_ORCO);
}
/* add an origspace layer if needed */
if ((md_datamask->mask.lmask) & CD_MASK_ORIGSPACE_MLOOP) {
if (!CustomData_has_layer(&mesh->corner_data, CD_ORIGSPACE_MLOOP)) {
CustomData_add_layer(
&mesh->corner_data, CD_ORIGSPACE_MLOOP, CD_SET_DEFAULT, mesh->corners_num);
mesh_init_origspace(*mesh);
/* add an origspace layer if needed */
if ((md_datamask->mask.lmask) & CD_MASK_ORIGSPACE_MLOOP) {
if (!CustomData_has_layer(&mesh->corner_data, CD_ORIGSPACE_MLOOP)) {
CustomData_add_layer(
&mesh->corner_data, CD_ORIGSPACE_MLOOP, CD_SET_DEFAULT, mesh->corners_num);
mesh_init_origspace(*mesh);
}
}
}
Mesh *mesh_next = modifier_modify_mesh_and_geometry_set(md, mectx, mesh, geometry_set_final);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
/* if the modifier returned a new mesh, release the old one */
if (mesh != mesh_next) {
BLI_assert(mesh != &mesh_input);
BKE_id_free(nullptr, mesh);
}
mesh = mesh_next;
}
modifier_modify_mesh_and_geometry_set(md, mectx, geometry_set);
/* create an orco mesh in parallel */
if (nextmask.vmask & CD_MASK_ORCO) {
@ -600,17 +551,14 @@ static void mesh_calc_modifiers(Depsgraph &depsgraph,
CustomData_MeshMasks_update(&temp_cddata_masks, &nextmask);
mesh_set_only_copy(mesh_orco, &temp_cddata_masks);
mesh_next = BKE_modifier_modify_mesh(md, &mectx_orco, mesh_orco);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
/* if the modifier returned a new mesh, release the old one */
if (mesh_orco != mesh_next) {
BLI_assert(mesh_orco != &mesh_input);
Mesh *mesh_orco_new = BKE_modifier_modify_mesh(md, &mectx_orco, mesh_orco);
ASSERT_IS_VALID_MESH(mesh_orco_new);
if (mesh_orco_new) {
if (mesh_orco != mesh_orco_new) {
BLI_assert(mesh_orco != mesh_input);
BKE_id_free(nullptr, mesh_orco);
}
mesh_orco = mesh_next;
mesh_orco = mesh_orco_new;
}
}
@ -626,21 +574,23 @@ static void mesh_calc_modifiers(Depsgraph &depsgraph,
nextmask.pmask |= CD_MASK_ORIGINDEX;
mesh_set_only_copy(mesh_orco_cloth, &nextmask);
mesh_next = BKE_modifier_modify_mesh(md, &mectx_orco, mesh_orco_cloth);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
/* if the modifier returned a new mesh, release the old one */
if (mesh_orco_cloth != mesh_next) {
BLI_assert(mesh_orco != &mesh_input);
Mesh *mesh_orco_cloth_new = BKE_modifier_modify_mesh(md, &mectx_orco, mesh_orco_cloth);
ASSERT_IS_VALID_MESH(mesh_orco_cloth_new);
if (mesh_orco_cloth_new) {
if (mesh_orco_cloth != mesh_orco_cloth_new) {
BLI_assert(mesh_orco != mesh_input);
BKE_id_free(nullptr, mesh_orco_cloth);
}
mesh_orco_cloth = mesh_next;
mesh_orco_cloth = mesh_orco_cloth_new;
}
}
mesh->runtime->deformed_only = false;
if (const Mesh *mesh = geometry_set.get_mesh()) {
if (mesh->runtime->deformed_only) {
Mesh *mesh_mut = geometry_set.get_mesh_for_write();
mesh_mut->runtime->deformed_only = false;
}
}
}
if (sculpt_mode && md->type == eModifierType_Multires) {
@ -654,32 +604,6 @@ static void mesh_calc_modifiers(Depsgraph &depsgraph,
BKE_modifier_free_temporary_data(md);
}
if (mesh == nullptr) {
if (allow_shared_mesh) {
mesh = &mesh_input;
}
else {
mesh = BKE_mesh_copy_for_eval(mesh_input);
}
}
/* Denotes whether the object which the modifier stack came from owns the mesh or whether the
* mesh is shared across multiple objects since there are no effective modifiers. */
const bool is_own_mesh = (mesh != &mesh_input);
/* Add orco coordinates to final and deformed mesh if requested. */
if (final_datamask.vmask & CD_MASK_ORCO) {
/* No need in ORCO layer if the mesh was not deformed or modified: undeformed mesh in this case
* matches input mesh. */
if (is_own_mesh) {
add_orco_mesh(ob, nullptr, *mesh, mesh_orco, CD_ORCO);
}
if (mesh_deform) {
add_orco_mesh(ob, nullptr, *mesh_deform, nullptr, CD_ORCO);
}
}
if (mesh_orco) {
BKE_id_free(nullptr, mesh_orco);
}
@ -689,44 +613,14 @@ static void mesh_calc_modifiers(Depsgraph &depsgraph,
/* Remove temporary data layer only needed for modifier evaluation.
* Save some memory, and ensure GPU subdivision does not need to deal with this. */
CustomData_free_layers(&mesh->vert_data, CD_CLOTH_ORCO, mesh->verts_num);
/* Compute normals. */
if (is_own_mesh) {
mesh_calc_finalize(mesh_input, *mesh);
}
else {
MeshRuntime *runtime = mesh_input.runtime;
if (runtime->mesh_eval == nullptr) {
std::lock_guard lock{mesh_input.runtime->eval_mutex};
if (runtime->mesh_eval == nullptr) {
/* Not yet finalized by any instance, do it now
* Isolate since computing normals is multithreaded and we are holding a lock. */
threading::isolate_task([&] {
mesh = BKE_mesh_copy_for_eval(mesh_input);
mesh_calc_finalize(mesh_input, *mesh);
runtime->mesh_eval = mesh;
});
}
else {
/* Already finalized by another instance, reuse. */
mesh = runtime->mesh_eval;
}
}
else {
/* Already finalized by another instance, reuse. */
mesh = runtime->mesh_eval;
if (const Mesh *mesh = geometry_set.get_mesh()) {
if (CustomData_has_layer(&mesh->vert_data, CD_CLOTH_ORCO)) {
Mesh *mesh_mut = geometry_set.get_mesh_for_write();
CustomData_free_layers(&mesh_mut->vert_data, CD_CLOTH_ORCO, mesh_mut->verts_num);
}
}
/* Return final mesh */
*r_final = mesh;
if (r_deform) {
*r_deform = mesh_deform;
}
if (r_geometry_set) {
*r_geometry_set = new GeometrySet(std::move(geometry_set_final));
}
return geometry_set;
}
bool editbmesh_modifier_is_enabled(const Scene *scene,
@ -766,21 +660,19 @@ static MutableSpan<float3> mesh_wrapper_vert_coords_ensure_for_write(Mesh *mesh)
return {};
}
static void editbmesh_calc_modifiers(Depsgraph &depsgraph,
const Scene &scene,
Object &ob,
const CustomData_MeshMasks &dataMask,
/* return args */
Mesh **r_cage,
Mesh **r_final,
GeometrySet **r_geometry_set)
static void save_cage_mesh(GeometrySet &geometry)
{
Mesh &mesh_input = *static_cast<Mesh *>(ob.data);
BMEditMesh &em_input = *mesh_input.runtime->edit_mesh;
MeshEditHints &edit_data = geometry_mesh_edit_hints_ensure(geometry);
edit_data.mesh_cage = geometry.get_component_ptr<MeshComponent>();
}
Mesh *mesh_cage = nullptr;
/* This geometry set contains the non-mesh data that might be generated by modifiers. */
GeometrySet geometry_set_final;
static GeometrySet editbmesh_calc_modifiers(Depsgraph &depsgraph,
const Scene &scene,
Object &ob,
const CustomData_MeshMasks &dataMask)
{
const Mesh &mesh_input = *static_cast<const Mesh *>(ob.data);
const BMEditMesh &em_input = *mesh_input.runtime->edit_mesh;
/* Mesh with constructive modifiers but no deformation applied. Tracked
* along with final mesh if undeformed / orco coordinates are requested
@ -810,12 +702,12 @@ static void editbmesh_calc_modifiers(Depsgraph &depsgraph,
CDMaskLink *md_datamask = datamasks;
CustomData_MeshMasks append_mask = CD_MASK_BAREMESH;
Mesh *mesh = BKE_mesh_wrapper_from_editmesh(
mesh_input.runtime->edit_mesh, &final_datamask, &mesh_input);
GeometrySet geometry_set = GeometrySet::from_mesh(
BKE_mesh_wrapper_from_editmesh(mesh_input.runtime->edit_mesh, &final_datamask, &mesh_input));
int cageIndex = BKE_modifiers_get_cage_index(&scene, &ob, nullptr, true);
if (r_cage && cageIndex == -1) {
mesh_cage = mesh;
if (cageIndex == -1) {
save_cage_mesh(geometry_set);
}
/* The mesh from edit mode should not have any original index layers already, since those
@ -828,8 +720,10 @@ static void editbmesh_calc_modifiers(Depsgraph &depsgraph,
BKE_modifiers_clear_errors(&ob);
if (ob.modifier_flag & OB_MODIFIER_FLAG_ADD_REST_POSITION) {
BKE_mesh_wrapper_ensure_mdata(mesh);
set_rest_position(*mesh);
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
BKE_mesh_wrapper_ensure_mdata(mesh);
set_rest_position(*mesh);
}
}
bool non_deform_modifier_applied = false;
@ -840,6 +734,7 @@ static void editbmesh_calc_modifiers(Depsgraph &depsgraph,
}
ScopedModifierTimer modifier_timer{*md};
Mesh *mesh = geometry_set.get_mesh_for_write();
/* Add an orco mesh as layer if needed by this modifier. */
if (mesh_orco && mti->required_data_mask) {
@ -850,21 +745,6 @@ static void editbmesh_calc_modifiers(Depsgraph &depsgraph,
}
}
if (mesh == mesh_cage) {
/* If the cage mesh has already been assigned, we have passed the cage index in the modifier
* list. If the cage and final meshes are still the same, duplicate the final mesh so the
* cage mesh isn't modified anymore. */
mesh = BKE_mesh_copy_for_eval(*mesh);
if (mesh_cage->runtime->edit_mesh) {
mesh->runtime->edit_mesh = mesh_cage->runtime->edit_mesh;
mesh->runtime->is_original_bmesh = true;
if (mesh_cage->runtime->edit_data) {
mesh->runtime->edit_data = std::make_unique<EditMeshData>(
*mesh_cage->runtime->edit_data);
}
}
}
if (mti->type == ModifierTypeType::OnlyDeform) {
if (mti->deform_verts_EM) {
BKE_modifier_deform_vertsEM(
@ -914,30 +794,25 @@ static void editbmesh_calc_modifiers(Depsgraph &depsgraph,
mask.emask |= CD_MASK_ORIGINDEX;
mask.pmask |= CD_MASK_ORIGINDEX;
mesh_set_only_copy(mesh, &mask);
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
mesh_set_only_copy(mesh, &mask);
if (mask.lmask & CD_MASK_ORIGSPACE_MLOOP) {
if (!CustomData_has_layer(&mesh->corner_data, CD_ORIGSPACE_MLOOP)) {
CustomData_add_layer(
&mesh->corner_data, CD_ORIGSPACE_MLOOP, CD_SET_DEFAULT, mesh->corners_num);
mesh_init_origspace(*mesh);
if (mask.lmask & CD_MASK_ORIGSPACE_MLOOP) {
if (!CustomData_has_layer(&mesh->corner_data, CD_ORIGSPACE_MLOOP)) {
CustomData_add_layer(
&mesh->corner_data, CD_ORIGSPACE_MLOOP, CD_SET_DEFAULT, mesh->corners_num);
mesh_init_origspace(*mesh);
}
}
mesh->runtime->deformed_only = false;
}
Mesh *mesh_next = modifier_modify_mesh_and_geometry_set(md, mectx, mesh, geometry_set_final);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
if (mesh != mesh_next) {
BKE_id_free(nullptr, mesh);
}
mesh = mesh_next;
}
mesh->runtime->deformed_only = false;
modifier_modify_mesh_and_geometry_set(md, mectx, geometry_set);
}
if (r_cage && i == cageIndex) {
mesh_cage = mesh;
if (i == cageIndex) {
blender::bke::save_cage_mesh(geometry_set);
}
}
@ -945,24 +820,18 @@ static void editbmesh_calc_modifiers(Depsgraph &depsgraph,
/* Add orco coordinates to final and deformed mesh if requested. */
if (final_datamask.vmask & CD_MASK_ORCO) {
/* FIXME(@ideasman42): avoid the need to convert to mesh data just to add an orco layer. */
BKE_mesh_wrapper_ensure_mdata(mesh);
add_orco_mesh(ob, &em_input, *mesh, mesh_orco, CD_ORCO);
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
/* FIXME(@ideasman42): avoid the need to convert to mesh data just to add an orco layer. */
BKE_mesh_wrapper_ensure_mdata(mesh);
add_orco_mesh(ob, &em_input, *mesh, mesh_orco, CD_ORCO);
}
}
if (mesh_orco) {
BKE_id_free(nullptr, mesh_orco);
}
/* Return final mesh. */
*r_final = mesh;
if (r_cage) {
*r_cage = mesh_cage;
}
if (r_geometry_set) {
*r_geometry_set = new GeometrySet(std::move(geometry_set_final));
}
return geometry_set;
}
static void mesh_build_extra_data(const Depsgraph &depsgraph,
@ -982,53 +851,26 @@ static void mesh_build_data(Depsgraph &depsgraph,
const CustomData_MeshMasks &dataMask,
const bool need_mapping)
{
#if 0 /* XXX This is already taken care of in #mesh_calc_modifiers... */
if (need_mapping) {
/* Also add the flag so that it is recorded in lastDataMask. */
dataMask->vmask |= CD_MASK_ORIGINDEX;
dataMask->emask |= CD_MASK_ORIGINDEX;
dataMask->pmask |= CD_MASK_ORIGINDEX;
}
#endif
const Mesh &mesh_input = *static_cast<const Mesh *>(ob.data);
GeometrySet geometry_set = mesh_calc_modifiers(
depsgraph, scene, ob, true, need_mapping, dataMask, true);
const Mesh *mesh_eval = geometry_set.get_mesh();
Mesh *mesh_eval = nullptr, *mesh_deform_eval = nullptr;
GeometrySet *geometry_set_eval = nullptr;
mesh_calc_modifiers(depsgraph,
scene,
ob,
true,
need_mapping,
dataMask,
true,
true,
&mesh_deform_eval,
&mesh_eval,
&geometry_set_eval);
BKE_object_eval_assign_data(&ob, &const_cast<ID &>(mesh_eval->id), false);
ob.runtime->geometry_set_eval = new GeometrySet(std::move(geometry_set));
/* The modifier stack evaluation is storing result in mesh->runtime.mesh_eval, but this result
* is not guaranteed to be owned by object.
*
* Check ownership now, since later on we can not go to a mesh owned by someone else via
* object's runtime: this could cause access freed data on depsgraph destruction (mesh who owns
* the final result might be freed prior to object). */
Mesh *mesh = (Mesh *)ob.data;
const bool is_mesh_eval_owned = (mesh_eval != mesh->runtime->mesh_eval);
BKE_object_eval_assign_data(&ob, &mesh_eval->id, is_mesh_eval_owned);
/* Add the final mesh as a non-owning component to the geometry set. */
MeshComponent &mesh_component = geometry_set_eval->get_component_for_write<MeshComponent>();
mesh_component.replace(mesh_eval, GeometryOwnershipType::Editable);
ob.runtime->geometry_set_eval = geometry_set_eval;
ob.runtime->mesh_deform_eval = mesh_deform_eval;
ob.runtime->last_data_mask = dataMask;
ob.runtime->last_need_mapping = need_mapping;
/* Make sure that drivers can target shapekey properties.
* Note that this causes a potential inconsistency, as the shapekey may have a
* different topology than the evaluated mesh. */
BLI_assert(mesh->key == nullptr || DEG_is_evaluated_id(&mesh->key->id));
mesh_eval->key = mesh->key;
if (mesh_input.key) {
BLI_assert(DEG_is_evaluated_id(&mesh_input->key->id));
if (mesh_eval != &mesh_input) {
const_cast<Mesh *>(mesh_eval)->key = mesh_input.key;
}
}
if ((ob.mode & OB_MODE_ALL_SCULPT) && ob.sculpt) {
if (DEG_is_active(&depsgraph)) {
@ -1044,43 +886,50 @@ static void editbmesh_build_data(Depsgraph &depsgraph,
Object &obedit,
CustomData_MeshMasks &dataMask)
{
Mesh *mesh = static_cast<Mesh *>(obedit.data);
Mesh *me_cage;
Mesh *me_final;
GeometrySet *non_mesh_components;
const Mesh &mesh_input = *static_cast<const Mesh *>(obedit.data);
editbmesh_calc_modifiers(
depsgraph, scene, obedit, dataMask, &me_cage, &me_final, &non_mesh_components);
/* The modifier stack result is expected to share edit mesh pointer with the input.
* This is similar `mesh_calc_finalize()`. */
BKE_mesh_free_editmesh(me_final);
BKE_mesh_free_editmesh(me_cage);
me_final->runtime->edit_mesh = me_cage->runtime->edit_mesh = mesh->runtime->edit_mesh;
GeometrySet geometry_set = editbmesh_calc_modifiers(depsgraph, scene, obedit, dataMask);
/* Object has edit_mesh but is not in edit mode (object shares mesh datablock with another object
* with is in edit mode).
* Convert edit mesh to mesh until the draw manager can draw mesh wrapper which is not in the
* edit mode. */
if (!(obedit.mode & OB_MODE_EDIT)) {
BKE_mesh_wrapper_ensure_mdata(me_final);
if (me_final != me_cage) {
BKE_mesh_wrapper_ensure_mdata(me_cage);
MeshEditHints &edit_hints =
*geometry_set.get_component_for_write<GeometryComponentEditData>().mesh_edit_hints_;
const bool cage_mesh_shared = edit_hints.mesh_cage->get() == geometry_set.get_mesh();
Mesh *mesh = geometry_set.get_mesh_for_write();
BKE_mesh_wrapper_ensure_mdata(mesh);
if (cage_mesh_shared) {
save_cage_mesh(geometry_set);
}
else {
if (edit_hints.mesh_cage->is_mutable()) {
edit_hints.mesh_cage->tag_ensured_mutable();
}
else {
Mesh *mesh_copy = BKE_mesh_copy_for_eval(*edit_hints.mesh_cage->get());
MeshComponent *copy = new MeshComponent(mesh_copy);
edit_hints.mesh_cage = ImplicitSharingPtr<MeshComponent>(copy);
}
Mesh *mesh_cage = const_cast<MeshComponent &>(*edit_hints.mesh_cage).get_for_write();
BKE_mesh_wrapper_ensure_mdata(mesh_cage);
}
}
const bool is_mesh_eval_owned = (me_final != mesh->runtime->mesh_eval);
BKE_object_eval_assign_data(&obedit, &me_final->id, is_mesh_eval_owned);
Mesh *mesh_final = const_cast<Mesh *>(geometry_set.get_mesh());
BKE_object_eval_assign_data(&obedit, &mesh_final->id, false);
/* Make sure that drivers can target shapekey properties.
* Note that this causes a potential inconsistency, as the shapekey may have a
* different topology than the evaluated mesh. */
BLI_assert(mesh->key == nullptr || DEG_is_evaluated_id(&mesh->key->id));
me_final->key = mesh->key;
BLI_assert(mesh_input.key == nullptr || DEG_is_evaluated_id(&mesh_input.key->id));
if (mesh_input.key && !mesh_final->key) {
mesh_final->key = mesh_input.key;
}
obedit.runtime->editmesh_eval_cage = me_cage;
obedit.runtime->geometry_set_eval = non_mesh_components;
obedit.runtime->geometry_set_eval = new GeometrySet(std::move(geometry_set));
obedit.runtime->last_data_mask = dataMask;
}
@ -1202,7 +1051,7 @@ Mesh *mesh_get_eval_deform(Depsgraph *depsgraph,
CustomData_MeshMasks cddata_masks = *dataMask;
object_get_datamask(*depsgraph, *ob, cddata_masks, &need_mapping);
if (!ob->runtime->mesh_deform_eval ||
if (!BKE_object_get_mesh_deform_eval(ob) ||
!CustomData_MeshMasks_are_matching(&(ob->runtime->last_data_mask), &cddata_masks) ||
(need_mapping && !ob->runtime->last_need_mapping))
{
@ -1211,7 +1060,7 @@ Mesh *mesh_get_eval_deform(Depsgraph *depsgraph,
*depsgraph, *scene, *ob, cddata_masks, need_mapping || ob->runtime->last_need_mapping);
}
return ob->runtime->mesh_deform_eval;
return const_cast<Mesh *>(BKE_object_get_mesh_deform_eval(ob));
}
Mesh *mesh_create_eval_final(Depsgraph *depsgraph,
@ -1219,10 +1068,10 @@ Mesh *mesh_create_eval_final(Depsgraph *depsgraph,
Object *ob,
const CustomData_MeshMasks *dataMask)
{
Mesh *result;
mesh_calc_modifiers(
*depsgraph, *scene, *ob, true, false, *dataMask, false, false, nullptr, &result, nullptr);
return result;
GeometrySet geometry_set = mesh_calc_modifiers(
*depsgraph, *scene, *ob, true, false, *dataMask, false);
geometry_set.ensure_owns_direct_data();
return geometry_set.get_component_for_write<MeshComponent>().release();
}
Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph,
@ -1230,10 +1079,10 @@ Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph,
Object *ob,
const CustomData_MeshMasks *dataMask)
{
Mesh *result;
mesh_calc_modifiers(
*depsgraph, *scene, *ob, false, false, *dataMask, false, false, nullptr, &result, nullptr);
return result;
GeometrySet geometry_set = mesh_calc_modifiers(
*depsgraph, *scene, *ob, false, false, *dataMask, false);
geometry_set.ensure_owns_direct_data();
return geometry_set.get_component_for_write<MeshComponent>().release();
}
Mesh *mesh_create_eval_no_deform_render(Depsgraph *depsgraph,
@ -1241,10 +1090,10 @@ Mesh *mesh_create_eval_no_deform_render(Depsgraph *depsgraph,
Object *ob,
const CustomData_MeshMasks *dataMask)
{
Mesh *result;
mesh_calc_modifiers(
*depsgraph, *scene, *ob, false, false, *dataMask, false, false, nullptr, &result, nullptr);
return result;
GeometrySet geometry_set = mesh_calc_modifiers(
*depsgraph, *scene, *ob, false, false, *dataMask, false);
geometry_set.ensure_owns_direct_data();
return geometry_set.get_component_for_write<MeshComponent>().release();
}
Mesh *editbmesh_get_eval_cage(Depsgraph *depsgraph,
@ -1260,13 +1109,13 @@ Mesh *editbmesh_get_eval_cage(Depsgraph *depsgraph,
*/
object_get_datamask(*depsgraph, *obedit, cddata_masks, nullptr);
if (!obedit->runtime->editmesh_eval_cage ||
if (!BKE_object_get_editmesh_eval_cage(obedit) ||
!CustomData_MeshMasks_are_matching(&(obedit->runtime->last_data_mask), &cddata_masks))
{
editbmesh_build_data(*depsgraph, *scene, *obedit, cddata_masks);
}
return obedit->runtime->editmesh_eval_cage;
return const_cast<Mesh *>(BKE_object_get_editmesh_eval_cage(obedit));
}
Mesh *editbmesh_get_eval_cage_from_orig(Depsgraph *depsgraph,

View File

@ -1640,13 +1640,6 @@ void BKE_object_free_derived_caches(Object *ob)
object_update_from_subsurf_ccg(ob);
if (ob->runtime->editmesh_eval_cage &&
ob->runtime->editmesh_eval_cage != reinterpret_cast<Mesh *>(ob->runtime->data_eval))
{
BKE_id_free(nullptr, ob->runtime->editmesh_eval_cage);
}
ob->runtime->editmesh_eval_cage = nullptr;
if (ob->runtime->data_eval != nullptr) {
if (ob->runtime->is_data_eval_owned) {
ID *data_eval = ob->runtime->data_eval;
@ -1661,11 +1654,6 @@ void BKE_object_free_derived_caches(Object *ob)
}
ob->runtime->data_eval = nullptr;
}
if (ob->runtime->mesh_deform_eval != nullptr) {
Mesh *mesh_deform_eval = ob->runtime->mesh_deform_eval;
BKE_id_free(nullptr, mesh_deform_eval);
ob->runtime->mesh_deform_eval = nullptr;
}
/* Restore initial pointer for copy-on-evaluation data-blocks, object->data
* might be pointing to an evaluated data-block data was just freed above. */
@ -1686,10 +1674,8 @@ void BKE_object_free_derived_caches(Object *ob)
ob->runtime->gpd_eval = nullptr;
}
if (ob->runtime->geometry_set_eval != nullptr) {
delete ob->runtime->geometry_set_eval;
ob->runtime->geometry_set_eval = nullptr;
}
delete ob->runtime->geometry_set_eval;
ob->runtime->geometry_set_eval = nullptr;
}
void BKE_object_free_caches(Object *object)
@ -4221,26 +4207,59 @@ const Mesh *BKE_object_get_editmesh_eval_final(const Object *object)
return nullptr;
}
return reinterpret_cast<Mesh *>(object->runtime->data_eval);
return reinterpret_cast<const Mesh *>(object->runtime->data_eval);
}
const Mesh *BKE_object_get_editmesh_eval_cage(const Object *object)
{
using namespace blender::bke;
BLI_assert(!DEG_is_original_id(&object->id));
BLI_assert(object->type == OB_MESH);
const Mesh *mesh = static_cast<const Mesh *>(object->data);
BLI_assert(mesh->runtime->edit_mesh != nullptr);
UNUSED_VARS_NDEBUG(mesh);
return object->runtime->editmesh_eval_cage;
const GeometrySet *geometry_set = object->runtime->geometry_set_eval;
if (!geometry_set) {
return nullptr;
}
const auto *component = geometry_set->get_component<GeometryComponentEditData>();
if (!component) {
return nullptr;
}
const MeshEditHints *edit_hints = component->mesh_edit_hints_.get();
if (!edit_hints) {
return nullptr;
}
const MeshComponent *mesh_component = edit_hints->mesh_cage.get();
if (!mesh_component) {
return nullptr;
}
return mesh_component->get();
}
const Mesh *BKE_object_get_mesh_deform_eval(const Object *object)
{
using namespace blender::bke;
BLI_assert(!DEG_is_original_id(&object->id));
BLI_assert(object->type == OB_MESH);
return object->runtime->mesh_deform_eval;
const GeometrySet *geometry_set = object->runtime->geometry_set_eval;
if (!geometry_set) {
return nullptr;
}
const auto *component = geometry_set->get_component<GeometryComponentEditData>();
if (!component) {
return nullptr;
}
const MeshEditHints *edit_hints = component->mesh_edit_hints_.get();
if (!edit_hints) {
return nullptr;
}
const MeshComponent *mesh_component = edit_hints->mesh_deform.get();
if (!mesh_component) {
return nullptr;
}
return mesh_component->get();
}
Lattice *BKE_object_get_lattice(const Object *object)
@ -4879,7 +4898,6 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int /*flag*/)
blender::bke::ObjectRuntime *runtime = object->runtime;
runtime->data_eval = nullptr;
runtime->gpd_eval = nullptr;
runtime->mesh_deform_eval = nullptr;
runtime->curve_cache = nullptr;
runtime->object_as_temp_mesh = nullptr;
runtime->pose_backup = nullptr;

View File

@ -13,6 +13,7 @@
#include "BLI_implicit_sharing.hh"
#include "BLI_struct_equality_utils.hh"
#include "BLI_utildefines.h"
namespace blender {
@ -89,6 +90,12 @@ template<typename T> class ImplicitSharingPtr {
return data_ != nullptr;
}
template<typename U, BLI_ENABLE_IF((std::is_convertible_v<T, U>))>
operator ImplicitSharingPtr<U>() const
{
return ImplicitSharingPtr<U>(static_cast<U *>(data_));
}
const T *get() const
{
return data_;