Files
test2/source/blender/editors/object/object_hook.cc

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

1002 lines
26 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
2011-02-27 20:29:51 +00:00
*/
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_armature_types.h"
#include "DNA_curve_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_action.h"
#include "BKE_context.hh"
2024-01-29 18:57:16 -05:00
#include "BKE_deform.hh"
#include "BKE_editmesh.hh"
2024-01-23 15:18:09 -05:00
#include "BKE_layer.hh"
#include "BKE_mesh_types.hh"
2023-11-14 09:30:40 +01:00
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "BKE_report.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_query.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.h"
#include "ED_curve.hh"
#include "ED_mesh.hh"
#include "ED_screen.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "UI_resources.hh"
#include "object_intern.hh"
namespace blender::ed::object {
static int return_editmesh_indexar(BMEditMesh *em,
int *r_indexar_num,
int **r_indexar,
float r_cent[3])
{
BMVert *eve;
BMIter iter;
int *index, nr, indexar_num = 0;
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2019-04-22 09:19:45 +10:00
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
indexar_num++;
2019-04-22 09:19:45 +10:00
}
}
if (indexar_num == 0) {
2012-04-28 15:42:27 +00:00
return 0;
2019-04-22 09:19:45 +10:00
}
*r_indexar = index = static_cast<int *>(MEM_mallocN(4 * indexar_num, "hook indexar"));
*r_indexar_num = indexar_num;
2012-04-28 15:42:27 +00:00
nr = 0;
zero_v3(r_cent);
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
2012-04-28 15:42:27 +00:00
*index = nr;
index++;
add_v3_v3(r_cent, eve->co);
}
nr++;
}
mul_v3_fl(r_cent, 1.0f / float(indexar_num));
return indexar_num;
}
static bool return_editmesh_vgroup(Object *obedit, BMEditMesh *em, char *r_name, float r_cent[3])
{
const int active_index = BKE_object_defgroup_active_index_get(obedit);
const int cd_dvert_offset = active_index ?
CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT) :
-1;
if (cd_dvert_offset != -1) {
const int defgrp_index = active_index - 1;
int indexar_num = 0;
MDeformVert *dvert;
BMVert *eve;
BMIter iter;
/* find the vertices */
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
dvert = static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
if (BKE_defvert_find_weight(dvert, defgrp_index) > 0.0f) {
add_v3_v3(r_cent, eve->co);
indexar_num++;
}
}
if (indexar_num) {
const ListBase *defbase = BKE_object_defgroup_list(obedit);
bDeformGroup *dg = static_cast<bDeformGroup *>(BLI_findlink(defbase, defgrp_index));
BLI_strncpy(r_name, dg->name, sizeof(dg->name));
mul_v3_fl(r_cent, 1.0f / float(indexar_num));
return true;
}
}
return false;
}
static void select_editbmesh_hook(Object *ob, HookModifierData *hmd)
{
Mesh *mesh = static_cast<Mesh *>(ob->data);
BMEditMesh *em = mesh->runtime->edit_mesh;
BMVert *eve;
BMIter iter;
2012-04-28 15:42:27 +00:00
int index = 0, nr = 0;
if (hmd->indexar == nullptr) {
return;
2019-04-22 09:19:45 +10:00
}
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2012-04-28 15:42:27 +00:00
if (nr == hmd->indexar[index]) {
BM_vert_select_set(em->bm, eve, true);
if (index < hmd->indexar_num - 1) {
2012-04-28 15:42:27 +00:00
index++;
2019-04-22 09:19:45 +10:00
}
}
nr++;
}
EDBM_select_flush(em);
}
static int return_editlattice_indexar(Lattice *editlatt,
int **r_indexar,
int *r_indexar_num,
float r_cent[3])
{
BPoint *bp;
int *index, nr, indexar_num = 0, a;
/* count */
2012-04-28 15:42:27 +00:00
a = editlatt->pntsu * editlatt->pntsv * editlatt->pntsw;
bp = editlatt->def;
while (a--) {
if (bp->f1 & SELECT) {
2019-04-22 09:19:45 +10:00
if (bp->hide == 0) {
indexar_num++;
2019-04-22 09:19:45 +10:00
}
}
bp++;
}
if (indexar_num == 0) {
2012-04-28 15:42:27 +00:00
return 0;
2019-04-22 09:19:45 +10:00
}
*r_indexar = index = static_cast<int *>(MEM_mallocN(4 * indexar_num, "hook indexar"));
*r_indexar_num = indexar_num;
2012-04-28 15:42:27 +00:00
nr = 0;
zero_v3(r_cent);
2012-04-28 15:42:27 +00:00
a = editlatt->pntsu * editlatt->pntsv * editlatt->pntsw;
bp = editlatt->def;
while (a--) {
if (bp->f1 & SELECT) {
2012-04-28 15:42:27 +00:00
if (bp->hide == 0) {
*index = nr;
index++;
add_v3_v3(r_cent, bp->vec);
}
}
bp++;
nr++;
}
mul_v3_fl(r_cent, 1.0f / float(indexar_num));
return indexar_num;
}
static void select_editlattice_hook(Object *obedit, HookModifierData *hmd)
{
Lattice *lt = static_cast<Lattice *>(obedit->data), *editlt;
BPoint *bp;
2012-04-28 15:42:27 +00:00
int index = 0, nr = 0, a;
2012-04-28 15:42:27 +00:00
editlt = lt->editlatt->latt;
/* count */
2012-04-28 15:42:27 +00:00
a = editlt->pntsu * editlt->pntsv * editlt->pntsw;
bp = editlt->def;
while (a--) {
2012-04-28 15:42:27 +00:00
if (hmd->indexar[index] == nr) {
bp->f1 |= SELECT;
if (index < hmd->indexar_num - 1) {
2012-04-28 15:42:27 +00:00
index++;
2019-04-22 09:19:45 +10:00
}
}
nr++;
bp++;
}
}
static int return_editcurve_indexar(Object *obedit,
int **r_indexar,
int *r_indexar_num,
float r_cent[3])
{
2012-04-28 15:42:27 +00:00
ListBase *editnurb = object_editcurve_get(obedit);
BPoint *bp;
BezTriple *bezt;
int *index, a, nr, indexar_num = 0;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->type == CU_BEZIER) {
2012-04-28 15:42:27 +00:00
bezt = nu->bezt;
a = nu->pntsu;
while (a--) {
2019-04-22 09:19:45 +10:00
if (bezt->f1 & SELECT) {
indexar_num++;
2019-04-22 09:19:45 +10:00
}
if (bezt->f2 & SELECT) {
indexar_num++;
2019-04-22 09:19:45 +10:00
}
if (bezt->f3 & SELECT) {
indexar_num++;
2019-04-22 09:19:45 +10:00
}
bezt++;
}
}
else {
2012-04-28 15:42:27 +00:00
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
2019-04-22 09:19:45 +10:00
if (bp->f1 & SELECT) {
indexar_num++;
2019-04-22 09:19:45 +10:00
}
bp++;
}
}
}
if (indexar_num == 0) {
2012-04-28 15:42:27 +00:00
return 0;
2019-04-22 09:19:45 +10:00
}
*r_indexar = index = static_cast<int *>(
MEM_mallocN(sizeof(*index) * indexar_num, "hook indexar"));
*r_indexar_num = indexar_num;
2012-04-28 15:42:27 +00:00
nr = 0;
zero_v3(r_cent);
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->type == CU_BEZIER) {
2012-04-28 15:42:27 +00:00
bezt = nu->bezt;
a = nu->pntsu;
while (a--) {
if (bezt->f1 & SELECT) {
2012-04-28 15:42:27 +00:00
*index = nr;
index++;
add_v3_v3(r_cent, bezt->vec[0]);
}
nr++;
if (bezt->f2 & SELECT) {
2012-04-28 15:42:27 +00:00
*index = nr;
index++;
add_v3_v3(r_cent, bezt->vec[1]);
}
nr++;
if (bezt->f3 & SELECT) {
2012-04-28 15:42:27 +00:00
*index = nr;
index++;
add_v3_v3(r_cent, bezt->vec[2]);
}
nr++;
bezt++;
}
}
else {
2012-04-28 15:42:27 +00:00
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
if (bp->f1 & SELECT) {
2012-04-28 15:42:27 +00:00
*index = nr;
index++;
add_v3_v3(r_cent, bp->vec);
}
nr++;
bp++;
}
}
}
mul_v3_fl(r_cent, 1.0f / float(indexar_num));
return indexar_num;
}
2018-06-06 15:50:24 +02:00
static bool object_hook_index_array(Main *bmain,
Scene *scene,
Object *obedit,
int **r_indexar,
int *r_indexar_num,
char *r_name,
float r_cent[3])
{
*r_indexar = nullptr;
*r_indexar_num = 0;
r_name[0] = 0;
switch (obedit->type) {
case OB_MESH: {
Mesh *mesh = static_cast<Mesh *>(obedit->data);
EDBM_mesh_load(bmain, obedit);
EDBM_mesh_make(obedit, scene->toolsettings->selectmode, true);
DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
BMEditMesh *em = mesh->runtime->edit_mesh;
BKE_editmesh_looptris_and_normals_calc(em);
/* check selected vertices first */
if (return_editmesh_indexar(em, r_indexar_num, r_indexar, r_cent) == 0) {
return return_editmesh_vgroup(obedit, em, r_name, r_cent);
}
return true;
}
case OB_CURVES_LEGACY:
case OB_SURF:
2018-06-06 15:50:24 +02:00
ED_curve_editnurb_load(bmain, obedit);
ED_curve_editnurb_make(obedit);
return return_editcurve_indexar(obedit, r_indexar, r_indexar_num, r_cent);
case OB_LATTICE: {
Lattice *lt = static_cast<Lattice *>(obedit->data);
return return_editlattice_indexar(lt->editlatt->latt, r_indexar, r_indexar_num, r_cent);
}
default:
return false;
}
}
static void select_editcurve_hook(Object *obedit, HookModifierData *hmd)
{
2012-04-28 15:42:27 +00:00
ListBase *editnurb = object_editcurve_get(obedit);
BPoint *bp;
BezTriple *bezt;
2012-04-28 15:42:27 +00:00
int index = 0, a, nr = 0;
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if (nu->type == CU_BEZIER) {
2012-04-28 15:42:27 +00:00
bezt = nu->bezt;
a = nu->pntsu;
while (a--) {
if (nr == hmd->indexar[index]) {
bezt->f1 |= SELECT;
if (index < hmd->indexar_num - 1) {
2012-04-28 15:42:27 +00:00
index++;
2019-04-22 09:19:45 +10:00
}
}
nr++;
if (nr == hmd->indexar[index]) {
bezt->f2 |= SELECT;
if (index < hmd->indexar_num - 1) {
2012-04-28 15:42:27 +00:00
index++;
2019-04-22 09:19:45 +10:00
}
}
nr++;
if (nr == hmd->indexar[index]) {
bezt->f3 |= SELECT;
if (index < hmd->indexar_num - 1) {
2012-04-28 15:42:27 +00:00
index++;
2019-04-22 09:19:45 +10:00
}
}
nr++;
bezt++;
}
}
else {
2012-04-28 15:42:27 +00:00
bp = nu->bp;
a = nu->pntsu * nu->pntsv;
while (a--) {
if (nr == hmd->indexar[index]) {
bp->f1 |= SELECT;
if (index < hmd->indexar_num - 1) {
2012-04-28 15:42:27 +00:00
index++;
2019-04-22 09:19:45 +10:00
}
}
nr++;
bp++;
}
}
}
}
static void object_hook_from_context(
bContext *C, PointerRNA *ptr, const int num, Object **r_ob, HookModifierData **r_hmd)
{
Object *ob;
HookModifierData *hmd;
if (ptr->data) { /* if modifier context is available, use that */
ob = (Object *)ptr->owner_id;
hmd = static_cast<HookModifierData *>(ptr->data);
}
else { /* use the provided property */
ob = CTX_data_edit_object(C);
hmd = (HookModifierData *)BLI_findlink(&ob->modifiers, num);
}
if (ob && hmd && (hmd->modifier.type == eModifierType_Hook)) {
*r_ob = ob;
*r_hmd = hmd;
}
else {
*r_ob = nullptr;
*r_hmd = nullptr;
}
}
static void object_hook_select(Object *ob, HookModifierData *hmd)
{
if (hmd->indexar == nullptr) {
return;
2019-04-22 09:19:45 +10:00
}
2019-04-22 09:19:45 +10:00
if (ob->type == OB_MESH) {
2012-04-28 15:42:27 +00:00
select_editbmesh_hook(ob, hmd);
2019-04-22 09:19:45 +10:00
}
else if (ob->type == OB_LATTICE) {
2012-04-28 15:42:27 +00:00
select_editlattice_hook(ob, hmd);
2019-04-22 09:19:45 +10:00
}
else if (ob->type == OB_CURVES_LEGACY) {
2012-04-28 15:42:27 +00:00
select_editcurve_hook(ob, hmd);
2019-04-22 09:19:45 +10:00
}
else if (ob->type == OB_SURF) {
2012-04-28 15:42:27 +00:00
select_editcurve_hook(ob, hmd);
2019-04-22 09:19:45 +10:00
}
}
/* special poll operators for hook operators */
/* TODO: check for properties window modifier context too as alternative? */
2018-07-02 11:47:00 +02:00
static bool hook_op_edit_poll(bContext *C)
{
2012-04-28 15:42:27 +00:00
Object *obedit = CTX_data_edit_object(C);
if (obedit) {
2019-04-22 09:19:45 +10:00
if (ED_operator_editmesh(C)) {
return true;
2019-04-22 09:19:45 +10:00
}
if (ED_operator_editsurfcurve(C)) {
return true;
2019-04-22 09:19:45 +10:00
}
if (ED_operator_editlattice(C)) {
return true;
2019-04-22 09:19:45 +10:00
}
// if (ED_operator_editmball(C)) return true;
}
return false;
}
static Object *add_hook_object_new(
ViewLayer: Lazy sync of scene data. When a change happens which invalidates view layers the syncing will be postponed until the first usage. This will improve importing or adding many objects in a single operation/script. `BKE_view_layer_need_resync_tag` is used to tag the view layer to be out of sync. Before accessing `BKE_view_layer_active_base_get`, `BKE_view_layer_active_object_get`, `BKE_view_layer_active_collection` or `BKE_view_layer_object_bases` the caller should call `BKE_view_layer_synced_ensure`. Having two functions ensures that partial syncing could be added as smaller patches in the future. Tagging a view layer out of sync could be replaced with a partial sync. Eventually the number of full resyncs could be reduced. After all tagging has been replaced with partial syncs the ensure_sync could be phased out. This patch has been added to discuss the details and consequences of the current approach. For clarity the call to BKE_view_layer_ensure_sync is placed close to the getters. In the future this could be placed in more strategical places to reduce the number of calls or improve performance. Finding those strategical places isn't that clear. When multiple operations are grouped in a single script you might want to always check for resync. Some areas found that can be improved. This list isn't complete. These areas aren't addressed by this patch as these changes would be hard to detect to the reviewer. The idea is to add changes to these areas as a separate patch. It might be that the initial commit would reduce performance compared to master, but will be fixed by the additional patches. **Object duplication** During object duplication the syncing is temporarily disabled. With this patch this isn't useful as when disabled the view_layer is accessed to locate bases. This can be improved by first locating the source bases, then duplicate and sync and locate the new bases. Will be solved in a separate patch for clarity reasons ({D15886}). **Object add** `BKE_object_add` not only adds a new object, but also selects and activates the new base. This requires the view_layer to be resynced. Some callers reverse the selection and activation (See `get_new_constraint_target`). We should make the selection and activation optional. This would make it possible to add multiple objects without having to resync per object. **Postpone Activate Base** Setting the basact is done in many locations. They follow a rule as after an action find the base and set the basact. Finding the base could require a resync. The idea is to store in the view_layer the object which base will be set in the basact during the next sync, reducing the times resyncing needs to happen. Reviewed By: mont29 Maniphest Tasks: T73411 Differential Revision: https://developer.blender.org/D15885
2022-09-14 21:33:51 +02:00
Main *bmain, Scene *scene, ViewLayer *view_layer, View3D *v3d, Object *obedit)
{
Base *basedit;
Object *ob;
ob = BKE_object_add(bmain, scene, view_layer, OB_EMPTY, nullptr);
ViewLayer: Lazy sync of scene data. When a change happens which invalidates view layers the syncing will be postponed until the first usage. This will improve importing or adding many objects in a single operation/script. `BKE_view_layer_need_resync_tag` is used to tag the view layer to be out of sync. Before accessing `BKE_view_layer_active_base_get`, `BKE_view_layer_active_object_get`, `BKE_view_layer_active_collection` or `BKE_view_layer_object_bases` the caller should call `BKE_view_layer_synced_ensure`. Having two functions ensures that partial syncing could be added as smaller patches in the future. Tagging a view layer out of sync could be replaced with a partial sync. Eventually the number of full resyncs could be reduced. After all tagging has been replaced with partial syncs the ensure_sync could be phased out. This patch has been added to discuss the details and consequences of the current approach. For clarity the call to BKE_view_layer_ensure_sync is placed close to the getters. In the future this could be placed in more strategical places to reduce the number of calls or improve performance. Finding those strategical places isn't that clear. When multiple operations are grouped in a single script you might want to always check for resync. Some areas found that can be improved. This list isn't complete. These areas aren't addressed by this patch as these changes would be hard to detect to the reviewer. The idea is to add changes to these areas as a separate patch. It might be that the initial commit would reduce performance compared to master, but will be fixed by the additional patches. **Object duplication** During object duplication the syncing is temporarily disabled. With this patch this isn't useful as when disabled the view_layer is accessed to locate bases. This can be improved by first locating the source bases, then duplicate and sync and locate the new bases. Will be solved in a separate patch for clarity reasons ({D15886}). **Object add** `BKE_object_add` not only adds a new object, but also selects and activates the new base. This requires the view_layer to be resynced. Some callers reverse the selection and activation (See `get_new_constraint_target`). We should make the selection and activation optional. This would make it possible to add multiple objects without having to resync per object. **Postpone Activate Base** Setting the basact is done in many locations. They follow a rule as after an action find the base and set the basact. Finding the base could require a resync. The idea is to store in the view_layer the object which base will be set in the basact during the next sync, reducing the times resyncing needs to happen. Reviewed By: mont29 Maniphest Tasks: T73411 Differential Revision: https://developer.blender.org/D15885
2022-09-14 21:33:51 +02:00
BKE_view_layer_synced_ensure(scene, view_layer);
Base *basact = BKE_view_layer_active_base_get(view_layer);
BLI_assert(basact->object == ob);
if (v3d && v3d->localvd) {
basact->local_view_bits |= v3d->local_view_uid;
}
/* icky, BKE_object_add sets new base as active.
* so set it back to the original edit object */
ViewLayer: Lazy sync of scene data. When a change happens which invalidates view layers the syncing will be postponed until the first usage. This will improve importing or adding many objects in a single operation/script. `BKE_view_layer_need_resync_tag` is used to tag the view layer to be out of sync. Before accessing `BKE_view_layer_active_base_get`, `BKE_view_layer_active_object_get`, `BKE_view_layer_active_collection` or `BKE_view_layer_object_bases` the caller should call `BKE_view_layer_synced_ensure`. Having two functions ensures that partial syncing could be added as smaller patches in the future. Tagging a view layer out of sync could be replaced with a partial sync. Eventually the number of full resyncs could be reduced. After all tagging has been replaced with partial syncs the ensure_sync could be phased out. This patch has been added to discuss the details and consequences of the current approach. For clarity the call to BKE_view_layer_ensure_sync is placed close to the getters. In the future this could be placed in more strategical places to reduce the number of calls or improve performance. Finding those strategical places isn't that clear. When multiple operations are grouped in a single script you might want to always check for resync. Some areas found that can be improved. This list isn't complete. These areas aren't addressed by this patch as these changes would be hard to detect to the reviewer. The idea is to add changes to these areas as a separate patch. It might be that the initial commit would reduce performance compared to master, but will be fixed by the additional patches. **Object duplication** During object duplication the syncing is temporarily disabled. With this patch this isn't useful as when disabled the view_layer is accessed to locate bases. This can be improved by first locating the source bases, then duplicate and sync and locate the new bases. Will be solved in a separate patch for clarity reasons ({D15886}). **Object add** `BKE_object_add` not only adds a new object, but also selects and activates the new base. This requires the view_layer to be resynced. Some callers reverse the selection and activation (See `get_new_constraint_target`). We should make the selection and activation optional. This would make it possible to add multiple objects without having to resync per object. **Postpone Activate Base** Setting the basact is done in many locations. They follow a rule as after an action find the base and set the basact. Finding the base could require a resync. The idea is to store in the view_layer the object which base will be set in the basact during the next sync, reducing the times resyncing needs to happen. Reviewed By: mont29 Maniphest Tasks: T73411 Differential Revision: https://developer.blender.org/D15885
2022-09-14 21:33:51 +02:00
basedit = BKE_view_layer_base_find(view_layer, obedit);
view_layer->basact = basedit;
return ob;
}
static int add_hook_object(const bContext *C,
Main *bmain,
Scene *scene,
ViewLayer *view_layer,
View3D *v3d,
Object *obedit,
Object *ob,
int mode,
ReportList *reports)
{
Refactor access to dependency graph This change ensures that operators which needs access to evaluated data first makes sure there is a dependency graph. Other accesses to the dependency graph made it more explicit about whether they just need a valid dependency graph pointer or whether they expect the graph to be already evaluated. This replaces OPTYPE_USE_EVAL_DATA which is now removed. Some general rules about usage of accessors: - Drawing is expected to happen from a fully evaluated dependency graph. There is now a function to access it, which will in the future control that dependency graph is actually evaluated. This check is not yet done because there are some things to be taken care about first: for example, post-update hooks might leave scene in a state where something is still tagged for update. - All operators which needs to access evaluated state must use CTX_data_ensure_evaluated_depsgraph(). This function replaces OPTYPE_USE_EVAL_DATA. The call is generally to be done in the very beginning of the operator, prior other logic (unless this is some comprehensive operator which might or might not need access to an evaluated state). This call is never to be used from a loop. If some utility function requires evaluated state of dependency graph the graph is to be passed as an explicit argument. This way it is clear that no evaluation happens in a loop or something like this. - All cases which needs to know dependency graph pointer, but which doesn't want to actually evaluate it can use old-style function CTX_data_depsgraph_pointer(), assuming that underlying code will ensure dependency graph is evaluated prior to accessing it. - The new functions are replacing OPTYPE_USE_EVAL_DATA, so now it is explicit and local about where dependency graph is being ensured. This commit also contains some fixes of wrong usage of evaluation functions on original objects. Ideally should be split out, but in reality with all the APIs being renamed is quite tricky. Fixes T67454: Blender crash on rapid undo and select Speculation here is that sometimes undo and selection operators are sometimes handled in the same event loop iteration, which leaves non-evaluated dependency graph. Fixes T67973: Crash on Fix Deforms operator Fixes T67902: Crash when undo a loop cut Reviewers: brecht Reviewed By: brecht Subscribers: lichtwerk Maniphest Tasks: T67454 Differential Revision: https://developer.blender.org/D5343
2019-07-25 16:36:22 +02:00
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ModifierData *md = nullptr;
HookModifierData *hmd = nullptr;
float cent[3];
float pose_mat[4][4];
int indexar_num, ok, *indexar;
char name[MAX_NAME];
ok = object_hook_index_array(bmain, scene, obedit, &indexar, &indexar_num, name, cent);
if (!ok) {
BKE_report(reports, RPT_ERROR, "Requires selected vertices or active vertex group");
return false;
}
2012-04-28 15:42:27 +00:00
if (mode == OBJECT_ADDHOOK_NEWOB && !ob) {
ob = add_hook_object_new(bmain, scene, view_layer, v3d, obedit);
/* transform cent to global coords for loc */
mul_v3_m4v3(ob->loc, obedit->object_to_world().ptr(), cent);
}
md = static_cast<ModifierData *>(obedit->modifiers.first);
while (md && BKE_modifier_get_info(ModifierType(md->type))->type == ModifierTypeType::OnlyDeform)
{
md = md->next;
}
hmd = (HookModifierData *)BKE_modifier_new(eModifierType_Hook);
BLI_insertlinkbefore(&obedit->modifiers, md, hmd);
2023-05-09 12:50:37 +10:00
SNPRINTF(hmd->modifier.name, "Hook-%s", ob->id.name + 2);
BKE_modifier_unique_name(&obedit->modifiers, (ModifierData *)hmd);
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
2024-02-06 17:10:40 +01:00
BKE_modifiers_persistent_uid_init(*obedit, hmd->modifier);
2012-04-28 15:42:27 +00:00
hmd->object = ob;
hmd->indexar = indexar;
copy_v3_v3(hmd->cent, cent);
hmd->indexar_num = indexar_num;
2023-05-09 12:50:37 +10:00
STRNCPY(hmd->name, name);
unit_m4(pose_mat);
invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
if (mode == OBJECT_ADDHOOK_NEWOB) {
/* pass */
}
else {
/* may overwrite with pose-bone location, below */
mul_v3_m4v3(cent, obedit->world_to_object().ptr(), ob->object_to_world().location());
}
if (mode == OBJECT_ADDHOOK_SELOB_BONE) {
bArmature *arm = static_cast<bArmature *>(ob->data);
BLI_assert(ob->type == OB_ARMATURE);
if (arm->act_bone) {
bPoseChannel *pchan_act;
2023-05-09 12:50:37 +10:00
STRNCPY(hmd->subtarget, arm->act_bone->name);
pchan_act = BKE_pose_channel_active_if_bonecoll_visible(ob);
if (LIKELY(pchan_act)) {
invert_m4_m4(pose_mat, pchan_act->pose_mat);
mul_v3_m4v3(cent, ob->object_to_world().ptr(), pchan_act->pose_mat[3]);
mul_v3_m4v3(cent, obedit->world_to_object().ptr(), cent);
}
}
else {
BKE_report(reports, RPT_WARNING, "Armature has no active object bone");
}
}
copy_v3_v3(hmd->cent, cent);
/* matrix calculus */
/* vert x (obmat x hook->world_to_object) x hook->object_to_world().ptr() x ob->world_to_object
*/
/* (parentinv) */
Refactor access to dependency graph This change ensures that operators which needs access to evaluated data first makes sure there is a dependency graph. Other accesses to the dependency graph made it more explicit about whether they just need a valid dependency graph pointer or whether they expect the graph to be already evaluated. This replaces OPTYPE_USE_EVAL_DATA which is now removed. Some general rules about usage of accessors: - Drawing is expected to happen from a fully evaluated dependency graph. There is now a function to access it, which will in the future control that dependency graph is actually evaluated. This check is not yet done because there are some things to be taken care about first: for example, post-update hooks might leave scene in a state where something is still tagged for update. - All operators which needs to access evaluated state must use CTX_data_ensure_evaluated_depsgraph(). This function replaces OPTYPE_USE_EVAL_DATA. The call is generally to be done in the very beginning of the operator, prior other logic (unless this is some comprehensive operator which might or might not need access to an evaluated state). This call is never to be used from a loop. If some utility function requires evaluated state of dependency graph the graph is to be passed as an explicit argument. This way it is clear that no evaluation happens in a loop or something like this. - All cases which needs to know dependency graph pointer, but which doesn't want to actually evaluate it can use old-style function CTX_data_depsgraph_pointer(), assuming that underlying code will ensure dependency graph is evaluated prior to accessing it. - The new functions are replacing OPTYPE_USE_EVAL_DATA, so now it is explicit and local about where dependency graph is being ensured. This commit also contains some fixes of wrong usage of evaluation functions on original objects. Ideally should be split out, but in reality with all the APIs being renamed is quite tricky. Fixes T67454: Blender crash on rapid undo and select Speculation here is that sometimes undo and selection operators are sometimes handled in the same event loop iteration, which leaves non-evaluated dependency graph. Fixes T67973: Crash on Fix Deforms operator Fixes T67902: Crash when undo a loop cut Reviewers: brecht Reviewed By: brecht Subscribers: lichtwerk Maniphest Tasks: T67454 Differential Revision: https://developer.blender.org/D5343
2019-07-25 16:36:22 +02:00
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
BKE_object_transform_copy(object_eval, ob);
BKE_object_where_is_calc(depsgraph, scene_eval, object_eval);
invert_m4_m4(object_eval->runtime->world_to_object.ptr(), object_eval->object_to_world().ptr());
/* apparently this call goes from right to left... */
mul_m4_series(hmd->parentinv,
pose_mat,
object_eval->world_to_object().ptr(),
obedit->object_to_world().ptr());
DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(bmain);
return true;
}
static int object_add_hook_selob_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *obedit = CTX_data_edit_object(C);
Object *obsel = nullptr;
2014-04-11 11:25:41 +10:00
const bool use_bone = RNA_boolean_get(op->ptr, "use_bone");
const int mode = use_bone ? OBJECT_ADDHOOK_SELOB_BONE : OBJECT_ADDHOOK_SELOB;
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob != obedit) {
obsel = ob;
break;
}
}
CTX_DATA_END;
if (!obsel) {
BKE_report(op->reports, RPT_ERROR, "Cannot add hook with no other selected objects");
return OPERATOR_CANCELLED;
}
if (use_bone && obsel->type != OB_ARMATURE) {
BKE_report(op->reports, RPT_ERROR, "Cannot add hook bone for a non armature object");
return OPERATOR_CANCELLED;
}
if (add_hook_object(C, bmain, scene, view_layer, nullptr, obedit, obsel, mode, op->reports)) {
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obedit);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_hook_add_selob(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Hook to Selected Object";
ot->description = "Hook selected vertices to the first selected object";
ot->idname = "OBJECT_OT_hook_add_selob";
/* api callbacks */
ot->exec = object_add_hook_selob_exec;
ot->poll = hook_op_edit_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna,
"use_bone",
false,
"Active Bone",
"Assign the hook to the hook object's active bone");
}
static int object_add_hook_newob_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
Object *obedit = CTX_data_edit_object(C);
if (add_hook_object(
C, bmain, scene, view_layer, v3d, obedit, nullptr, OBJECT_ADDHOOK_NEWOB, op->reports))
{
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obedit);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_hook_add_newob(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Hook to New Object";
ot->description = "Hook selected vertices to a newly created object";
ot->idname = "OBJECT_OT_hook_add_newob";
/* api callbacks */
ot->exec = object_add_hook_newob_exec;
ot->poll = hook_op_edit_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int object_hook_remove_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = CTX_data_edit_object(C);
HookModifierData *hmd = nullptr;
hmd = (HookModifierData *)BLI_findlink(&ob->modifiers, num);
if (!hmd) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
/* remove functionality */
BKE_modifier_remove_from_list(ob, (ModifierData *)hmd);
BKE_modifier_free((ModifierData *)hmd);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
static const EnumPropertyItem *hook_mod_itemf(bContext *C,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
Object *ob = CTX_data_edit_object(C);
EnumPropertyItem tmp = {0, "", 0, "", ""};
EnumPropertyItem *item = nullptr;
ModifierData *md = nullptr;
2012-04-28 15:42:27 +00:00
int a, totitem = 0;
2019-04-22 09:19:45 +10:00
if (!ob) {
return rna_enum_dummy_NULL_items;
2019-04-22 09:19:45 +10:00
}
for (a = 0, md = static_cast<ModifierData *>(ob->modifiers.first); md; md = md->next, a++) {
2012-04-28 15:42:27 +00:00
if (md->type == eModifierType_Hook) {
tmp.value = a;
tmp.icon = ICON_HOOK;
2012-04-28 15:42:27 +00:00
tmp.identifier = md->name;
tmp.name = md->name;
RNA_enum_item_add(&item, &totitem, &tmp);
}
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
void OBJECT_OT_hook_remove(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Remove Hook";
ot->idname = "OBJECT_OT_hook_remove";
ot->description = "Remove a hook from the active object";
/* api callbacks */
ot->exec = object_hook_remove_exec;
ot->invoke = WM_menu_invoke;
ot->poll = hook_op_edit_poll;
/* flags */
/* this operator removes modifier which isn't stored in local undo stack, * so redoing it from
* redo panel gives totally weird results. */
2012-04-28 15:42:27 +00:00
ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO;
/* properties */
2012-04-28 15:42:27 +00:00
prop = RNA_def_enum(
ot->srna, "modifier", rna_enum_dummy_NULL_items, 0, "Modifier", "Modifier number to remove");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
}
static int object_hook_reset_exec(bContext *C, wmOperator *op)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_HookModifier);
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = nullptr;
HookModifierData *hmd = nullptr;
object_hook_from_context(C, &ptr, num, &ob, &hmd);
if (hmd == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
BKE_object_modifier_hook_reset(ob, hmd);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
void OBJECT_OT_hook_reset(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Reset Hook";
ot->description = "Recalculate and clear offset transformation";
ot->idname = "OBJECT_OT_hook_reset";
/* callbacks */
ot->exec = object_hook_reset_exec;
ot->poll = hook_op_edit_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna,
"modifier",
rna_enum_dummy_NULL_items,
0,
"Modifier",
"Modifier number to assign to");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
static int object_hook_recenter_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_HookModifier);
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = nullptr;
HookModifierData *hmd = nullptr;
Scene *scene = CTX_data_scene(C);
float bmat[3][3], imat[3][3];
object_hook_from_context(C, &ptr, num, &ob, &hmd);
if (hmd == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
/* recenter functionality */
copy_m3_m4(bmat, ob->object_to_world().ptr());
invert_m3_m3(imat, bmat);
sub_v3_v3v3(hmd->cent, scene->cursor.location, ob->object_to_world().location());
mul_m3_v3(imat, hmd->cent);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
void OBJECT_OT_hook_recenter(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Recenter Hook";
ot->description = "Set hook center to cursor position";
ot->idname = "OBJECT_OT_hook_recenter";
/* callbacks */
ot->exec = object_hook_recenter_exec;
ot->poll = hook_op_edit_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna,
"modifier",
rna_enum_dummy_NULL_items,
0,
"Modifier",
"Modifier number to assign to");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
static int object_hook_assign_exec(bContext *C, wmOperator *op)
{
2018-06-06 15:50:24 +02:00
Main *bmain = CTX_data_main(C);
2012-04-28 15:42:27 +00:00
Scene *scene = CTX_data_scene(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_HookModifier);
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = nullptr;
HookModifierData *hmd = nullptr;
float cent[3];
char name[MAX_NAME];
int *indexar, indexar_num;
object_hook_from_context(C, &ptr, num, &ob, &hmd);
if (hmd == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
/* assign functionality */
if (!object_hook_index_array(bmain, scene, ob, &indexar, &indexar_num, name, cent)) {
BKE_report(op->reports, RPT_WARNING, "Requires selected vertices or active vertex group");
return OPERATOR_CANCELLED;
}
2019-04-22 09:19:45 +10:00
if (hmd->indexar) {
MEM_freeN(hmd->indexar);
2019-04-22 09:19:45 +10:00
}
copy_v3_v3(hmd->cent, cent);
2012-04-28 15:42:27 +00:00
hmd->indexar = indexar;
hmd->indexar_num = indexar_num;
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
void OBJECT_OT_hook_assign(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Assign to Hook";
ot->description = "Assign the selected vertices to a hook";
ot->idname = "OBJECT_OT_hook_assign";
/* callbacks */
ot->exec = object_hook_assign_exec;
ot->poll = hook_op_edit_poll;
/* flags */
/* this operator changes data stored in modifier which doesn't get pushed to undo stack, * so
* redoing it from redo panel gives totally weird results. */
2012-04-28 15:42:27 +00:00
ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna,
"modifier",
rna_enum_dummy_NULL_items,
0,
"Modifier",
"Modifier number to assign to");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
static int object_hook_select_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_HookModifier);
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = nullptr;
HookModifierData *hmd = nullptr;
object_hook_from_context(C, &ptr, num, &ob, &hmd);
if (hmd == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
/* select functionality */
object_hook_select(ob, hmd);
DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
return OPERATOR_FINISHED;
}
void OBJECT_OT_hook_select(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Select Hook";
ot->description = "Select affected vertices on mesh";
ot->idname = "OBJECT_OT_hook_select";
/* callbacks */
ot->exec = object_hook_select_exec;
ot->poll = hook_op_edit_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
2012-04-28 15:42:27 +00:00
prop = RNA_def_enum(
ot->srna, "modifier", rna_enum_dummy_NULL_items, 0, "Modifier", "Modifier number to remove");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
} // namespace blender::ed::object