diff --git a/source/blender/depsgraph/DEG_depsgraph_query.hh b/source/blender/depsgraph/DEG_depsgraph_query.hh index 580c08593a9..332f7a5afa8 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.hh +++ b/source/blender/depsgraph/DEG_depsgraph_query.hh @@ -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 * \{ */ diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index ecbac37ae75..58bf5bf91b8 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -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); +}