Modifiers: add unique modifier identifiers
This adds a new `ModifierData.persistent_uid` integer property with the following properties: * It's unique within the object. * Match between the original and evaluated object. * Stable across Blender sessions. * Stable across renames and reorderings of modifiers. Potential use-cases: * Everywhere where we currently use the name as identifier. For example, `ModifierComputeContext` and `ModifierViewerPathElem`. * Can be used as part of a key in `IDCacheKey` to support caches that stay in-tact across undo steps. * Can be stored in the `SpaceNode` to identify the modifier whose geometry node tree is currently pinned (this could use the name currently, but that hasn't been implemented yet). This new identifier has some overlap with `ModifierData.session_uid`, but there are some differences: * `session_uid` is unique within the entire Blender session (except for duplicates between the original and evaluated data blocks). * `session_uid` is not stable across Blender sessions. Especially due to the first difference, it's not immediately obvious that the new `persistent_uid` can fulfill all use-cases of the existing `session_uid`. Nevertheless, this seems likely and will be cleaned up separately. Unfortunately, there is not a single place where modifiers are added to objects currently. Therefore, there are quite a few places that need to ensure valid identifiers. I tried to catch all the places, but it's hard to be sure. Therefore, I added an assert in `object_copy_data` that checks if all identifiers are valid. This way, we should be notified relatively quickly if issues are caused by invalid identifiers. Pull Request: https://projects.blender.org/blender/blender/pulls/117347
This commit is contained in:
@@ -29,7 +29,7 @@ extern "C" {
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 19
|
||||
#define BLENDER_FILE_SUBVERSION 20
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
||||
@@ -473,7 +473,20 @@ void BKE_modifiers_foreach_tex_link(Object *ob, TexWalkFunc walk, void *user_dat
|
||||
ModifierData *BKE_modifiers_findby_type(const Object *ob, ModifierType type);
|
||||
ModifierData *BKE_modifiers_findby_name(const Object *ob, const char *name);
|
||||
ModifierData *BKE_modifiers_findby_session_uid(const Object *ob, const SessionUID *session_uid);
|
||||
ModifierData *BKE_modifiers_findby_persistent_uid(const Object *ob, int persistent_uid);
|
||||
|
||||
void BKE_modifiers_clear_errors(Object *ob);
|
||||
|
||||
/**
|
||||
* Updates `md.persistent_uid` so that it is a valid identifier (>=1) and is unique in the object.
|
||||
*/
|
||||
void BKE_modifiers_persistent_uid_init(const Object &object, ModifierData &md);
|
||||
/**
|
||||
* Returns true when all the modifier identifiers are positive and unique. This should generally be
|
||||
* true and should only be used by asserts.
|
||||
*/
|
||||
bool BKE_modifiers_persistent_uids_are_valid(const Object &object);
|
||||
|
||||
/**
|
||||
* used for buttons, to find out if the 'draw deformed in edit-mode option is there.
|
||||
*
|
||||
|
||||
@@ -115,7 +115,8 @@ bool BKE_object_copy_gpencil_modifier(Object *ob_dst, GpencilModifierData *gmd_s
|
||||
* Copy the whole stack of modifiers from one object into another.
|
||||
*
|
||||
* \warning *Does not* clear modifier stack and related data (particle systems, soft-body,
|
||||
* etc.) in `ob_dst`, if needed calling code must do it.
|
||||
* etc.) in `ob_dst`, if needed calling code must do it. The caller is also responsible for
|
||||
* ensuring the modifier identifiers are unique.
|
||||
*
|
||||
* \param do_copy_all: If true, even modifiers that should not support copying (like Hook one)
|
||||
* will be duplicated.
|
||||
|
||||
@@ -2273,6 +2273,7 @@ void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
|
||||
else {
|
||||
BLI_addtail(&object->modifiers, md);
|
||||
}
|
||||
BKE_modifiers_persistent_uid_init(*object, md->modifier);
|
||||
|
||||
md->settings.properties = bke::idprop::create_group("Nodes Modifier Settings").release();
|
||||
IDProperty *angle_prop =
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_rand.hh"
|
||||
#include "BLI_session_uid.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
@@ -276,6 +277,16 @@ ModifierData *BKE_modifiers_findby_session_uid(const Object *ob, const SessionUI
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModifierData *BKE_modifiers_findby_persistent_uid(const Object *ob, const int persistent_uid)
|
||||
{
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->persistent_uid == persistent_uid) {
|
||||
return md;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BKE_modifiers_clear_errors(Object *ob)
|
||||
{
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
@@ -358,6 +369,7 @@ void BKE_modifier_copydata_ex(const ModifierData *md, ModifierData *target, cons
|
||||
target->mode = md->mode;
|
||||
target->flag = md->flag;
|
||||
target->ui_expand_flag = md->ui_expand_flag;
|
||||
target->persistent_uid = md->persistent_uid;
|
||||
|
||||
if (mti->copy_data) {
|
||||
mti->copy_data(md, target, flag);
|
||||
@@ -1020,6 +1032,48 @@ void BKE_modifier_check_uids_unique_and_report(const Object *object)
|
||||
BLI_gset_free(used_uids, nullptr);
|
||||
}
|
||||
|
||||
void BKE_modifiers_persistent_uid_init(const Object &object, ModifierData &md)
|
||||
{
|
||||
uint64_t hash = blender::get_default_hash(blender::StringRef(md.name));
|
||||
if (ID_IS_LINKED(&object)) {
|
||||
hash = blender::get_default_hash(hash, blender::StringRef(object.id.lib->filepath_abs));
|
||||
}
|
||||
if (ID_IS_OVERRIDE_LIBRARY_REAL(&object)) {
|
||||
BLI_assert(ID_IS_LINKED(object.id.override_library->reference));
|
||||
hash = blender::get_default_hash(
|
||||
hash, blender::StringRef(object.id.override_library->reference->lib->filepath_abs));
|
||||
}
|
||||
blender::RandomNumberGenerator rng{uint32_t(hash)};
|
||||
while (true) {
|
||||
const int new_uid = rng.get_int32();
|
||||
if (new_uid <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (BKE_modifiers_findby_persistent_uid(&object, new_uid) != nullptr) {
|
||||
continue;
|
||||
}
|
||||
md.persistent_uid = new_uid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool BKE_modifiers_persistent_uids_are_valid(const Object &object)
|
||||
{
|
||||
blender::Set<int> uids;
|
||||
int modifiers_num = 0;
|
||||
LISTBASE_FOREACH (const ModifierData *, md, &object.modifiers) {
|
||||
if (md->persistent_uid <= 0) {
|
||||
return false;
|
||||
}
|
||||
uids.add(md->persistent_uid);
|
||||
modifiers_num++;
|
||||
}
|
||||
if (uids.size() != modifiers_num) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BKE_modifier_blend_write(BlendWriter *writer, const ID *id_owner, ListBase *modbase)
|
||||
{
|
||||
if (modbase == nullptr) {
|
||||
|
||||
@@ -250,6 +250,7 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in
|
||||
BLI_listbase_clear(&ob_dst->greasepencil_modifiers);
|
||||
/* NOTE: Also takes care of soft-body and particle systems copying. */
|
||||
BKE_object_modifier_stack_copy(ob_dst, ob_src, true, flag_subdata);
|
||||
BLI_assert(BKE_modifiers_persistent_uids_are_valid(*ob_dst));
|
||||
|
||||
BLI_listbase_clear((ListBase *)&ob_dst->drawdata);
|
||||
BLI_listbase_clear(&ob_dst->pc_ids);
|
||||
@@ -766,6 +767,7 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
wmd->width = wav->width;
|
||||
|
||||
BLI_addtail(&ob->modifiers, wmd);
|
||||
BKE_modifiers_persistent_uid_init(*ob, wmd->modifier);
|
||||
|
||||
BLI_remlink(&ob->effect, paf);
|
||||
MEM_freeN(paf);
|
||||
@@ -784,6 +786,7 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
bmd->seed = 1;
|
||||
|
||||
BLI_addtail(&ob->modifiers, bmd);
|
||||
BKE_modifiers_persistent_uid_init(*ob, bmd->modifier);
|
||||
|
||||
BLI_remlink(&ob->effect, paf);
|
||||
MEM_freeN(paf);
|
||||
@@ -871,6 +874,7 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
BLI_remlink(&ob->hooks, hook);
|
||||
|
||||
BKE_modifier_unique_name(&ob->modifiers, (ModifierData *)hmd);
|
||||
BKE_modifiers_persistent_uid_init(*ob, hmd->modifier);
|
||||
|
||||
MEM_freeN(hook);
|
||||
}
|
||||
@@ -1423,6 +1427,7 @@ bool BKE_object_copy_modifier(
|
||||
|
||||
BLI_addtail(&ob_dst->modifiers, md_dst);
|
||||
BKE_modifier_unique_name(&ob_dst->modifiers, md_dst);
|
||||
BKE_modifiers_persistent_uid_init(*ob_dst, *md_dst);
|
||||
}
|
||||
|
||||
BKE_object_modifier_set_active(ob_dst, md_dst);
|
||||
|
||||
@@ -3958,6 +3958,7 @@ static ModifierData *object_add_or_copy_particle_system(
|
||||
psmd->psys = psys;
|
||||
BLI_addtail(&ob->modifiers, md);
|
||||
BKE_object_modifier_set_active(ob, md);
|
||||
BKE_modifiers_persistent_uid_init(*ob, *md);
|
||||
|
||||
psys->totpart = 0;
|
||||
psys->flag = PSYS_CURRENT;
|
||||
|
||||
@@ -2876,6 +2876,17 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 401, 20)) {
|
||||
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
|
||||
int uid = 1;
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
/* These identifiers are not necessarily stable for linked data. If the linked data has a
|
||||
* new modifier inserted, the identifiers of other modifiers can change. */
|
||||
md->persistent_uid = uid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
||||
@@ -551,6 +551,7 @@ static int add_hook_object(const bContext *C,
|
||||
BLI_insertlinkbefore(&obedit->modifiers, md, hmd);
|
||||
SNPRINTF(hmd->modifier.name, "Hook-%s", ob->id.name + 2);
|
||||
BKE_modifier_unique_name(&obedit->modifiers, (ModifierData *)hmd);
|
||||
BKE_modifiers_persistent_uid_init(*obedit, hmd->modifier);
|
||||
|
||||
hmd->object = ob;
|
||||
hmd->indexar = indexar;
|
||||
|
||||
@@ -197,6 +197,7 @@ ModifierData *ED_object_modifier_add(
|
||||
else {
|
||||
BLI_addtail(&ob->modifiers, new_md);
|
||||
}
|
||||
BKE_modifiers_persistent_uid_init(*ob, *new_md);
|
||||
|
||||
if (name) {
|
||||
STRNCPY_UTF8(new_md->name, name);
|
||||
@@ -1234,6 +1235,7 @@ bool ED_object_modifier_copy(
|
||||
BKE_modifier_copydata(md, nmd);
|
||||
BLI_insertlinkafter(&ob->modifiers, md, nmd);
|
||||
BKE_modifier_unique_name(&ob->modifiers, nmd);
|
||||
BKE_modifiers_persistent_uid_init(*ob, *nmd);
|
||||
BKE_object_modifier_set_active(ob, nmd);
|
||||
|
||||
nmd->flag |= eModifierFlag_OverrideLibrary_Local;
|
||||
@@ -3024,6 +3026,7 @@ static int skin_armature_create_exec(bContext *C, wmOperator *op)
|
||||
if (arm_md) {
|
||||
skin_md = edit_modifier_property_get(op, ob, eModifierType_Skin);
|
||||
BLI_insertlinkafter(&ob->modifiers, skin_md, arm_md);
|
||||
BKE_modifiers_persistent_uid_init(*arm_ob, arm_md->modifier);
|
||||
|
||||
arm_md->object = arm_ob;
|
||||
arm_md->deformflag = ARM_DEF_VGROUP | ARM_DEF_QUATERNION;
|
||||
|
||||
@@ -1151,6 +1151,7 @@ static bool copy_particle_systems_to_object(const bContext *C,
|
||||
psmd = (ParticleSystemModifierData *)md;
|
||||
/* push on top of the stack, no use trying to reproduce old stack order */
|
||||
BLI_addtail(&ob_to->modifiers, md);
|
||||
BKE_modifiers_persistent_uid_init(*ob_to, *md);
|
||||
|
||||
SNPRINTF(md->name, "ParticleSystem %i", i);
|
||||
BKE_modifier_unique_name(&ob_to->modifiers, (ModifierData *)psmd);
|
||||
|
||||
@@ -270,6 +270,7 @@ void AbcObjectReader::addCacheModifier()
|
||||
{
|
||||
ModifierData *md = BKE_modifier_new(eModifierType_MeshSequenceCache);
|
||||
BLI_addtail(&m_object->modifiers, md);
|
||||
BKE_modifiers_persistent_uid_init(*m_object, *md);
|
||||
|
||||
MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ void USDGeomReader::add_cache_modifier()
|
||||
|
||||
ModifierData *md = BKE_modifier_new(eModifierType_MeshSequenceCache);
|
||||
BLI_addtail(&object_->modifiers, md);
|
||||
BKE_modifiers_persistent_uid_init(*object_, *md);
|
||||
|
||||
MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
|
||||
|
||||
@@ -42,6 +43,7 @@ void USDGeomReader::add_subdiv_modifier()
|
||||
{
|
||||
ModifierData *md = BKE_modifier_new(eModifierType_Subsurf);
|
||||
BLI_addtail(&object_->modifiers, md);
|
||||
BKE_modifiers_persistent_uid_init(*object_, *md);
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
||||
|
||||
@@ -1080,6 +1080,7 @@ void import_mesh_skel_bindings(Main *bmain,
|
||||
if (!BKE_modifiers_findby_type(mesh_obj, eModifierType_Armature)) {
|
||||
ModifierData *md = BKE_modifier_new(eModifierType_Armature);
|
||||
BLI_addtail(&mesh_obj->modifiers, md);
|
||||
BKE_modifiers_persistent_uid_init(*mesh_obj, *md);
|
||||
}
|
||||
|
||||
/* Create a deform group per joint. */
|
||||
|
||||
@@ -136,7 +136,18 @@ typedef struct ModifierData {
|
||||
* and easily conflict with the explicit mapping of bits to panels here.
|
||||
*/
|
||||
uint16_t layout_panel_open_flag;
|
||||
char _pad[6];
|
||||
char _pad[2];
|
||||
/**
|
||||
* Uniquely identifies the modifier within the object. This identifier is stable across Blender
|
||||
* sessions. Modifiers on the original and corresponding evaluated object have matching
|
||||
* identifiers. The identifier stays the same if the modifier is renamed or moved in the modifier
|
||||
* stack.
|
||||
*
|
||||
* A valid identifier is non-negative (>= 1). Modifiers that are currently not on an object may
|
||||
* have invalid identifiers. It has to be initialized with #BKE_modifiers_persistent_uid_init
|
||||
* when it is added to an object.
|
||||
*/
|
||||
int persistent_uid;
|
||||
/** MAX_NAME. */
|
||||
char name[64];
|
||||
|
||||
|
||||
@@ -8523,6 +8523,13 @@ void RNA_def_modifier(BlenderRNA *brna)
|
||||
"Time in seconds that the modifier took to evaluate. This is only set on evaluated objects. "
|
||||
"If multiple modifiers run in parallel, execution time is not a reliable metric");
|
||||
|
||||
prop = RNA_def_property(srna, "persistent_uid", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Persistent UID",
|
||||
"Uniquely identifies the modifier within the modifier stack that it is part of");
|
||||
|
||||
/* types */
|
||||
rna_def_modifier_subsurf(brna);
|
||||
rna_def_modifier_lattice(brna);
|
||||
|
||||
Reference in New Issue
Block a user