Depsgraph: Expose helper functions for manual dupli iteration

Refactor some internal DEG_iterator code into public functions.
This shouldn't cause any functional change, but it's required for
#140378.

Pull Request: https://projects.blender.org/blender/blender/pulls/140674
This commit is contained in:
Miguel Pozo
2025-08-21 15:47:29 +02:00
parent 7d7562e849
commit 13bbf3ba44
2 changed files with 165 additions and 78 deletions

View File

@@ -313,6 +313,59 @@ void DEG_iterator_objects_end(BLI_Iterator *iter);
/** \} */
/* -------------------------------------------------------------------- */
/** \name DEG object iterators - Manual dupli iteration
* Helper functions for handling dupli instances iteration manually (ie. without passing
* DEG_ITER_OBJECT_FLAG_DUPLI to DEGObjectIterSettings::flags)
* \{ */
/**
* Returns true if the object should be visible on the given context.
*/
bool DEG_iterator_object_is_visible(eEvaluationMode eval_mode, const Object *ob);
/**
* Returns true if the dupli instance should be visible on the given context.
*/
bool DEG_iterator_dupli_is_visible(const DupliObject *dupli, eEvaluationMode eval_mode);
namespace evil {
/**
* WARNING: DON'T USE!!!
*
* These functions are exposed publicly as a temporary measure while we figure out how to fully get
* rid of temporary objects in the Draw module (See #144811).
*
* DON'T ADD NEW USE CASES FOR THESE FUNCTIONS.
*/
/**
* WARNING: DON'T USE!!!
* Generates a temporary object for a given dupli instance.
*
* Returns true if the resulting object should be visible, otherwise the temp object should be
* considered invalid.
* NOTE: DEG_iterator_temp_object_free_properties should be called regardless.
*
* \param do_matrix_setup: If false, the temp_object won't have valid
* object_to_world/world_to_object matrices, and the OB_NEG_SCALE flag will never be set.
*/
[[nodiscard]] bool DEG_iterator_temp_object_from_dupli(const Object *dupli_parent,
const DupliObject *dupli,
eEvaluationMode eval_mode,
bool do_matrix_setup,
Object *r_temp_object,
ObjectRuntimeHandle *r_temp_runtime);
/**
* WARNING: DON'T USE!!!
* Frees any property allocated when calling DEG_iterator_temp_object_from_dupli.
*/
void DEG_iterator_temp_object_free_properties(const DupliObject *dupli, Object *temp_object);
} // namespace evil
/** \} */
/* -------------------------------------------------------------------- */
/** \name DEG ID iterators
* \{ */

View File

@@ -48,6 +48,9 @@
namespace deg = blender::deg;
using evil::DEG_iterator_temp_object_free_properties;
using evil::DEG_iterator_temp_object_from_dupli;
/* ************************ DEG ITERATORS ********************* */
namespace {
@@ -62,37 +65,7 @@ void deg_invalidate_iterator_work_data(DEGObjectIterData *data)
#endif
}
void ensure_id_properties_freed(const IDProperty *dupli_idprops, IDProperty **temp_dupli_idprops)
{
if (*temp_dupli_idprops == nullptr) {
/* No ID properties in temp data-block -- no leak is possible. */
return;
}
if (*temp_dupli_idprops == dupli_idprops) {
/* Temp copy of object did not modify ID properties. */
return;
}
/* Free memory which is owned by temporary storage which is about to get overwritten. */
IDP_FreeProperty(*temp_dupli_idprops);
*temp_dupli_idprops = nullptr;
}
void free_owned_memory(DEGObjectIterData *data)
{
if (data->dupli_object_current == nullptr) {
/* We didn't enter duplication yet, so we can't have any dangling pointers. */
return;
}
const Object *dupli_object = data->dupli_object_current->ob;
Object *temp_dupli_object = &data->temp_dupli_object;
ensure_id_properties_freed(dupli_object->id.properties, &temp_dupli_object->id.properties);
ensure_id_properties_freed(dupli_object->id.system_properties,
&temp_dupli_object->id.system_properties);
}
bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject *dob)
bool deg_object_hide_original(eEvaluationMode eval_mode, const Object *ob, const DupliObject *dob)
{
/* Automatic hiding if this object is being instanced on verts/faces/frames
* by its parent. Ideally this should not be needed, but due to the wrong
@@ -141,16 +114,11 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data)
data->dupli_object_next_index = -1;
}
if (dob->no_draw) {
continue;
}
if (dob->ob_data && GS(dob->ob_data->name) == ID_MB) {
continue;
}
if (obd->type != OB_MBALL && deg_object_hide_original(data->eval_mode, dob->ob, dob)) {
if (!DEG_iterator_dupli_is_visible(dob, data->eval_mode)) {
continue;
}
/* TODO: Can this be removed? included_objects is already passed to object_duplilist(). */
DEGObjectIterSettings *settings = data->settings;
if (settings->included_objects) {
Object *object_orig = DEG_get_original(obd);
@@ -159,51 +127,26 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data)
}
}
free_owned_memory(data);
if (data->dupli_object_current) {
DEG_iterator_temp_object_free_properties(data->dupli_object_current,
&data->temp_dupli_object);
}
data->dupli_object_current = dob;
/* Temporary object to evaluate. */
Object *dupli_parent = data->dupli_parent;
Object *temp_dupli_object = &data->temp_dupli_object;
*temp_dupli_object = blender::dna::shallow_copy(*dob->ob);
temp_dupli_object->runtime = &data->temp_dupli_object_runtime;
*temp_dupli_object->runtime = *dob->ob->runtime;
temp_dupli_object->base_flag = dupli_parent->base_flag | BASE_FROM_DUPLI;
temp_dupli_object->base_local_view_bits = dupli_parent->base_local_view_bits;
temp_dupli_object->runtime->local_collections_bits =
dupli_parent->runtime->local_collections_bits;
temp_dupli_object->dt = std::min(temp_dupli_object->dt, dupli_parent->dt);
copy_v4_v4(temp_dupli_object->color, dupli_parent->color);
temp_dupli_object->runtime->select_id = dupli_parent->runtime->select_id;
if (dob->ob->data != dob->ob_data) {
BKE_object_replace_data_on_shallow_copy(temp_dupli_object, dob->ob_data);
if (DEG_iterator_temp_object_from_dupli(data->dupli_parent,
data->dupli_object_current,
data->eval_mode,
true,
&data->temp_dupli_object,
&data->temp_dupli_object_runtime))
{
data->next_object = &data->temp_dupli_object;
return true;
}
/* Duplicated elements shouldn't care whether their original collection is visible or not. */
temp_dupli_object->base_flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT;
int ob_visibility = BKE_object_visibility(temp_dupli_object, data->eval_mode);
if ((ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) == 0) {
continue;
}
/* This could be avoided by refactoring make_dupli() in order to track all negative scaling
* recursively. */
bool is_neg_scale = is_negative_m4(dob->mat);
SET_FLAG_FROM_TEST(data->temp_dupli_object.transflag, is_neg_scale, OB_NEG_SCALE);
copy_m4_m4(data->temp_dupli_object.runtime->object_to_world.ptr(), dob->mat);
invert_m4_m4(data->temp_dupli_object.runtime->world_to_object.ptr(),
data->temp_dupli_object.object_to_world().ptr());
data->next_object = &data->temp_dupli_object;
BLI_assert(deg::deg_validate_eval_copy_datablock(&data->temp_dupli_object.id));
return true;
}
free_owned_memory(data);
DEG_iterator_temp_object_free_properties(data->dupli_object_current, &data->temp_dupli_object);
data->dupli_list.clear();
data->dupli_parent = nullptr;
data->dupli_object_next = nullptr;
@@ -284,7 +227,7 @@ bool deg_iterator_objects_step(DEGObjectIterData *data)
if (data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) {
ob_visibility = BKE_object_visibility(object, data->eval_mode);
if (object->type != OB_MBALL && deg_object_hide_original(data->eval_mode, object, nullptr)) {
if (!DEG_iterator_object_is_visible(data->eval_mode, object)) {
continue;
}
}
@@ -509,3 +452,94 @@ void DEG_iterator_ids_next(BLI_Iterator *iter)
}
void DEG_iterator_ids_end(BLI_Iterator * /*iter*/) {}
bool DEG_iterator_object_is_visible(eEvaluationMode eval_mode, const Object *ob)
{
if (ob->type == OB_MBALL) {
return true;
}
return !deg_object_hide_original(eval_mode, ob, nullptr);
}
bool DEG_iterator_dupli_is_visible(const DupliObject *dupli, eEvaluationMode eval_mode)
{
if (dupli->no_draw) {
return false;
}
if (dupli->ob_data && GS(dupli->ob_data->name) == ID_MB) {
return false;
}
if (dupli->ob->type != OB_MBALL && deg_object_hide_original(eval_mode, dupli->ob, dupli)) {
return false;
}
return true;
}
bool evil::DEG_iterator_temp_object_from_dupli(const Object *dupli_parent,
const DupliObject *dupli,
eEvaluationMode eval_mode,
bool do_matrix_setup,
Object *r_temp_object,
ObjectRuntimeHandle *r_temp_runtime)
{
*r_temp_object = blender::dna::shallow_copy(*dupli->ob);
r_temp_object->runtime = r_temp_runtime;
*r_temp_object->runtime = *dupli->ob->runtime;
r_temp_object->base_flag = dupli_parent->base_flag | BASE_FROM_DUPLI;
r_temp_object->base_local_view_bits = dupli_parent->base_local_view_bits;
r_temp_object->runtime->local_collections_bits = dupli_parent->runtime->local_collections_bits;
r_temp_object->dt = std::min(r_temp_object->dt, dupli_parent->dt);
copy_v4_v4(r_temp_object->color, dupli_parent->color);
r_temp_object->runtime->select_id = dupli_parent->runtime->select_id;
if (dupli->ob->data != dupli->ob_data) {
BKE_object_replace_data_on_shallow_copy(r_temp_object, dupli->ob_data);
}
/* Duplicated elements shouldn't care whether their original collection is visible or not. */
r_temp_object->base_flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT;
/* TODO: Could this be computed in DEG_iterator_dupli_is_visible,
* before setting up the shallow copy? */
int ob_visibility = BKE_object_visibility(r_temp_object, eval_mode);
if ((ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) == 0) {
return false;
}
if (do_matrix_setup) {
/* This could be avoided by refactoring make_dupli() in order to track all negative scaling
* recursively. */
bool is_neg_scale = is_negative_m4(dupli->mat);
SET_FLAG_FROM_TEST(r_temp_object->transflag, is_neg_scale, OB_NEG_SCALE);
copy_m4_m4(r_temp_object->runtime->object_to_world.ptr(), dupli->mat);
invert_m4_m4(r_temp_object->runtime->world_to_object.ptr(),
r_temp_object->object_to_world().ptr());
}
BLI_assert(deg::deg_validate_eval_copy_datablock(&r_temp_object->id));
return true;
}
void ensure_id_properties_freed(const IDProperty *dupli_idprops, IDProperty **temp_dupli_idprops)
{
if (*temp_dupli_idprops == nullptr) {
/* No ID properties in temp data-block -- no leak is possible. */
return;
}
if (*temp_dupli_idprops == dupli_idprops) {
/* Temp copy of object did not modify ID properties. */
return;
}
/* Free memory which is owned by temporary storage which is about to get overwritten. */
IDP_FreeProperty(*temp_dupli_idprops);
*temp_dupli_idprops = nullptr;
}
void evil::DEG_iterator_temp_object_free_properties(const DupliObject *dupli, Object *temp_object)
{
ensure_id_properties_freed(dupli->ob->id.properties, &temp_object->id.properties);
ensure_id_properties_freed(dupli->ob->id.system_properties, &temp_object->id.system_properties);
}