2023-05-31 16:19:06 +02:00
|
|
|
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup edobj
|
2018-09-02 18:28:27 +10:00
|
|
|
*/
|
2011-02-27 20:29:51 +00:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
#include <cmath>
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdlib>
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2022-12-14 19:12:17 -06:00
|
|
|
#include "CLG_log.h"
|
|
|
|
|
|
2009-05-21 15:34:09 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
#include "DNA_anim_types.h"
|
2012-05-22 15:29:57 +00:00
|
|
|
#include "DNA_armature_types.h"
|
Geometry Nodes: support baking data block references
With this patch, materials are kept intact in simulation zones and bake nodes
without any additional user action.
This implements the design proposed in #108410 to support referencing
data-blocks (only materials for now) in the baked data. The task also describes
why this is not a trivial issue. A previous attempt was implemented in #109703
but it didn't work well-enough.
The solution is to have an explicit `name (+ library name) -> data-block`
mapping that is stored in the modifier for each bake node and simulation zone.
The `library name` is necessary for it to be unique within a .blend file. Note
that this refers to the name of the `Library` data-block and not a file path.
The baked data only contains the names of the used data-blocks. When the baked
data is loaded, the correct material data-block is looked up from the mapping.
### Automatic Mapping Generation
The most tricky aspect of this approach is to make it feel mostly automatic.
From the user point-of-view, it should just work. Therefore, we don't want the
user to have to create the mapping manually in the majority of cases. Creating
the mapping automatically is difficult because the data-blocks that should
become part of the mapping are only known during depsgraph evaluation. So we
somehow have to gather the missing data blocks during evaluation and then write
the new mappings back to the original data.
While writing back to original data is something we do in some cases already,
the situation here is different, because we are actually creating new relations
between data-blocks. This also means that we'll have to do user-counting. Since
user counts in data-blocks are *not* atomic, we can't do that from multiple
threads at the same time. Also, under some circumstances, it may be necessary to
trigger depsgraph evaluation again after the write-back because it actually
affects the result.
To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It
allows gathering tasks which write back to original data in a synchronous way
which may also require a reevaluation.
### Accessing the Mapping
A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier.
This map allows getting the `ID` pointer that should be used for a specific
data-block name that is stored in baked data. It's also used to gather all the
missing data mappings during evaluation.
### Weak ID References
The baked/cached geometries may have references to other data-blocks (currently
only materials, but in the future also e.g. instanced objects/collections).
However, the pointers of these data-blocks are not stable over time. That is
especially true when storing/loading the data from disk, but also just when
playing back the animation. Therefore, the used data-blocks have to referenced
in a different way at run-time.
This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the
run-time data of various geometry data-blocks. If the data-block is cached over
a longer period of time (such that material pointers can't be used directly), it
stores the material name (+ library name) used by each material slot. When the
geometry is used again, the material pointers are restored using these weak name
references and the `BakeDataBlockMap`.
### Manual Mapping Management
There is a new `Data-Blocks` panel in the bake settings in the node editor
sidebar that allows inspecting and modifying the data-blocks that are used when
baking. The user can change what data-block a specific name is mapped to.
Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
|
|
|
#include "DNA_array_utils.hh"
|
2009-05-21 15:34:09 +00:00
|
|
|
#include "DNA_curve_types.h"
|
2024-02-08 15:35:20 +01:00
|
|
|
#include "DNA_defaults.h"
|
2020-12-28 11:17:49 -06:00
|
|
|
#include "DNA_dynamicpaint_types.h"
|
|
|
|
|
#include "DNA_fluid_types.h"
|
2009-11-22 13:44:09 +00:00
|
|
|
#include "DNA_key_types.h"
|
2020-10-27 13:27:08 +11:00
|
|
|
#include "DNA_lattice_types.h"
|
2024-08-13 15:02:25 +02:00
|
|
|
#include "DNA_material_types.h"
|
2009-05-21 15:34:09 +00:00
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
|
#include "DNA_meshdata_types.h"
|
2018-02-07 11:14:08 +11:00
|
|
|
#include "DNA_object_force_types.h"
|
2022-10-12 19:16:22 -05:00
|
|
|
#include "DNA_pointcloud_types.h"
|
2009-04-27 18:05:58 +00:00
|
|
|
#include "DNA_scene_types.h"
|
2020-12-02 13:35:07 +01:00
|
|
|
#include "DNA_space_types.h"
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
#include "BLI_bitmap.h"
|
2009-04-27 18:05:58 +00:00
|
|
|
#include "BLI_listbase.h"
|
2024-09-26 21:13:39 +10:00
|
|
|
#include "BLI_path_utils.hh"
|
2009-11-28 13:33:56 +00:00
|
|
|
#include "BLI_string.h"
|
2012-05-20 19:49:27 +00:00
|
|
|
#include "BLI_string_utf8.h"
|
2024-02-08 15:35:20 +01:00
|
|
|
#include "BLI_string_utils.hh"
|
2011-01-07 18:36:47 +00:00
|
|
|
#include "BLI_utildefines.h"
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
#include "BKE_animsys.h"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_armature.hh"
|
|
|
|
|
#include "BKE_context.hh"
|
|
|
|
|
#include "BKE_curve.hh"
|
Curves: Rename "Hair" types, variables, and functions to "Curves"
Based on discussions from T95355 and T94193, the plan is to use
the name "Curves" to describe the data-block container for multiple
curves. Eventually this will replace the existing "Curve" data-block.
However, it will be a while before the curve data-block can be replaced
so in order to distinguish the two curve types in the UI, "Hair Curves"
will be used, but eventually changed back to "Curves".
This patch renames "hair-related" files, functions, types, and variable
names to this convention. A deep rename is preferred to keep code
consistent and to avoid any "hair" terminology from leaking, since the
new data-block is meant for all curve types, not just hair use cases.
The downside of this naming is that the difference between "Curve"
and "Curves" has become important. That was considered during
design discussons and deemed acceptable, especially given the
non-permanent nature of the somewhat common conflict.
Some points of interest:
- All DNA compatibility is lost, just like rBf59767ff9729.
- I renamed `ID_HA` to `ID_CV` so there is no complete mismatch.
- `hair_curves` is used where necessary to distinguish from the
existing "curves" plural.
- I didn't rename any of the cycles/rendering code function names,
since that is also used by the old hair particle system.
Differential Revision: https://developer.blender.org/D14007
2022-02-07 11:55:54 -06:00
|
|
|
#include "BKE_curves.h"
|
2022-04-26 08:07:54 -05:00
|
|
|
#include "BKE_curves.hh"
|
2018-11-07 18:00:24 +01:00
|
|
|
#include "BKE_displist.h"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_editmesh.hh"
|
2009-07-02 19:41:31 +00:00
|
|
|
#include "BKE_effect.h"
|
2022-04-26 08:06:04 -05:00
|
|
|
#include "BKE_geometry_set.hh"
|
2024-02-10 18:25:14 +01:00
|
|
|
#include "BKE_global.hh"
|
2024-08-13 15:02:25 +02:00
|
|
|
#include "BKE_grease_pencil.hh"
|
2024-03-26 12:57:30 -04:00
|
|
|
#include "BKE_idprop.hh"
|
2024-01-30 14:42:07 -05:00
|
|
|
#include "BKE_key.hh"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_lattice.hh"
|
2024-01-23 15:18:09 -05:00
|
|
|
#include "BKE_layer.hh"
|
2024-01-15 12:44:04 -05:00
|
|
|
#include "BKE_lib_id.hh"
|
2023-12-01 19:43:16 +01:00
|
|
|
#include "BKE_main.hh"
|
2021-05-20 12:15:57 +02:00
|
|
|
#include "BKE_material.h"
|
2024-01-24 11:33:47 -05:00
|
|
|
#include "BKE_mball.hh"
|
2023-03-12 22:29:15 +01:00
|
|
|
#include "BKE_mesh.hh"
|
2023-08-02 22:14:18 +02:00
|
|
|
#include "BKE_mesh_mapping.hh"
|
|
|
|
|
#include "BKE_mesh_runtime.hh"
|
2023-11-14 09:30:40 +01:00
|
|
|
#include "BKE_modifier.hh"
|
2023-08-02 22:14:18 +02:00
|
|
|
#include "BKE_multires.hh"
|
2023-10-09 23:41:53 +02:00
|
|
|
#include "BKE_object.hh"
|
2014-11-18 23:52:17 +01:00
|
|
|
#include "BKE_object_deform.h"
|
2023-11-15 18:46:07 +01:00
|
|
|
#include "BKE_object_types.hh"
|
2011-11-13 12:17:27 +00:00
|
|
|
#include "BKE_ocean.h"
|
2023-08-02 22:14:18 +02:00
|
|
|
#include "BKE_paint.hh"
|
2009-05-21 15:34:09 +00:00
|
|
|
#include "BKE_particle.h"
|
2024-01-11 10:54:47 +01:00
|
|
|
#include "BKE_pointcloud.hh"
|
2024-02-10 18:34:29 +01:00
|
|
|
#include "BKE_report.hh"
|
2024-02-10 19:16:25 +01:00
|
|
|
#include "BKE_scene.hh"
|
2009-07-02 19:41:31 +00:00
|
|
|
#include "BKE_softbody.h"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_volume.hh"
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2024-02-24 01:38:34 +01:00
|
|
|
#include "BLT_translation.hh"
|
|
|
|
|
|
2023-09-22 03:18:17 +02:00
|
|
|
#include "DEG_depsgraph.hh"
|
|
|
|
|
#include "DEG_depsgraph_build.hh"
|
|
|
|
|
#include "DEG_depsgraph_query.hh"
|
2017-06-08 10:14:53 +02:00
|
|
|
|
2024-02-09 18:59:42 +01:00
|
|
|
#include "BLT_translation.hh"
|
2022-02-04 07:31:53 -06:00
|
|
|
|
2023-08-10 22:40:27 +02:00
|
|
|
#include "RNA_access.hh"
|
|
|
|
|
#include "RNA_define.hh"
|
|
|
|
|
#include "RNA_enum_types.hh"
|
2024-07-10 18:30:02 +02:00
|
|
|
#include "RNA_prototypes.hh"
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "ED_armature.hh"
|
|
|
|
|
#include "ED_object.hh"
|
2024-03-26 21:51:09 -04:00
|
|
|
#include "ED_object_vgroup.hh"
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "ED_screen.hh"
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2023-12-05 15:05:28 +01:00
|
|
|
#include "ANIM_bone_collections.hh"
|
2023-07-27 14:14:40 +02:00
|
|
|
|
2024-09-27 13:17:18 +02:00
|
|
|
#include "GEO_merge_layers.hh"
|
|
|
|
|
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "UI_interface.hh"
|
2020-06-29 15:00:25 -04:00
|
|
|
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "WM_api.hh"
|
|
|
|
|
#include "WM_types.hh"
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2024-03-26 20:34:48 -04:00
|
|
|
#include "object_intern.hh"
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
namespace blender::ed::object {
|
Mesh: Remove redundant custom data pointers
For copy-on-write, we want to share attribute arrays between meshes
where possible. Mutable pointers like `Mesh.mvert` make that difficult
by making ownership vague. They also make code more complex by adding
redundancy.
The simplest solution is just removing them and retrieving layers from
`CustomData` as needed. Similar changes have already been applied to
curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of
the pointers generally makes code more obvious and more reusable.
Mesh data is now accessed with a C++ API (`Mesh::edges()` or
`Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`).
The CoW changes this commit makes possible are described in T95845
and T95842, and started in D14139 and D14140. The change also simplifies
the ongoing mesh struct-of-array refactors from T95965.
**RNA/Python Access Performance**
Theoretically, accessing mesh elements with the RNA API may become
slower, since the layer needs to be found on every random access.
However, overhead is already high enough that this doesn't make a
noticible differenc, and performance is actually improved in some
cases. Random access can be up to 10% faster, but other situations
might be a bit slower. Generally using `foreach_get/set` are the best
way to improve performance. See the differential revision for more
discussion about Python performance.
Cycles has been updated to use raw pointers and the internal Blender
mesh types, mostly because there is no sense in having this overhead
when it's already compiled with Blender. In my tests this roughly
halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million
face grid).
Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
|
|
|
|
2022-12-14 19:12:17 -06:00
|
|
|
static CLG_LogRef LOG = {"ed.object"};
|
|
|
|
|
|
2023-06-03 08:36:28 +10:00
|
|
|
static void modifier_skin_customdata_delete(Object *ob);
|
2012-05-22 15:29:37 +00:00
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Public Api
|
|
|
|
|
* \{ */
|
2009-05-21 15:34:09 +00:00
|
|
|
|
2019-04-04 14:42:33 +02:00
|
|
|
static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object *ob)
|
2018-10-12 18:39:24 +11:00
|
|
|
{
|
2019-04-04 14:42:33 +02:00
|
|
|
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
|
|
|
|
|
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
|
|
|
|
|
BKE_object_eval_reset(ob_eval);
|
2018-10-12 18:39:24 +11:00
|
|
|
if (ob->type == OB_MESH) {
|
2024-05-20 12:26:07 -04:00
|
|
|
Mesh *mesh_eval = blender::bke::mesh_create_eval_final(
|
|
|
|
|
depsgraph, scene_eval, ob_eval, &CD_MASK_DERIVEDMESH);
|
2024-04-18 13:52:20 +02:00
|
|
|
BKE_id_free(nullptr, mesh_eval);
|
2018-10-12 18:39:24 +11:00
|
|
|
}
|
|
|
|
|
else if (ob->type == OB_LATTICE) {
|
2019-04-04 14:42:33 +02:00
|
|
|
BKE_lattice_modifiers_calc(depsgraph, scene_eval, ob_eval);
|
2018-10-12 18:39:24 +11:00
|
|
|
}
|
|
|
|
|
else if (ob->type == OB_MBALL) {
|
2022-08-17 10:20:25 -04:00
|
|
|
BKE_mball_data_update(depsgraph, scene_eval, ob_eval);
|
2018-10-12 18:39:24 +11:00
|
|
|
}
|
2022-02-18 09:50:29 -06:00
|
|
|
else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) {
|
2021-06-28 15:14:43 -05:00
|
|
|
BKE_displist_make_curveTypes(depsgraph, scene_eval, ob_eval, false);
|
2018-10-12 18:39:24 +11:00
|
|
|
}
|
Curves: Rename "Hair" types, variables, and functions to "Curves"
Based on discussions from T95355 and T94193, the plan is to use
the name "Curves" to describe the data-block container for multiple
curves. Eventually this will replace the existing "Curve" data-block.
However, it will be a while before the curve data-block can be replaced
so in order to distinguish the two curve types in the UI, "Hair Curves"
will be used, but eventually changed back to "Curves".
This patch renames "hair-related" files, functions, types, and variable
names to this convention. A deep rename is preferred to keep code
consistent and to avoid any "hair" terminology from leaking, since the
new data-block is meant for all curve types, not just hair use cases.
The downside of this naming is that the difference between "Curve"
and "Curves" has become important. That was considered during
design discussons and deemed acceptable, especially given the
non-permanent nature of the somewhat common conflict.
Some points of interest:
- All DNA compatibility is lost, just like rBf59767ff9729.
- I renamed `ID_HA` to `ID_CV` so there is no complete mismatch.
- `hair_curves` is used where necessary to distinguish from the
existing "curves" plural.
- I didn't rename any of the cycles/rendering code function names,
since that is also used by the old hair particle system.
Differential Revision: https://developer.blender.org/D14007
2022-02-07 11:55:54 -06:00
|
|
|
else if (ob->type == OB_CURVES) {
|
|
|
|
|
BKE_curves_data_update(depsgraph, scene_eval, ob);
|
2020-03-17 14:41:48 +01:00
|
|
|
}
|
|
|
|
|
else if (ob->type == OB_POINTCLOUD) {
|
|
|
|
|
BKE_pointcloud_data_update(depsgraph, scene_eval, ob);
|
|
|
|
|
}
|
|
|
|
|
else if (ob->type == OB_VOLUME) {
|
|
|
|
|
BKE_volume_data_update(depsgraph, scene_eval, ob);
|
|
|
|
|
}
|
2018-10-12 18:39:24 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-04 14:42:33 +02:00
|
|
|
static void object_force_modifier_bind_simple_options(Depsgraph *depsgraph,
|
|
|
|
|
Object *object,
|
|
|
|
|
ModifierData *md)
|
|
|
|
|
{
|
2020-05-08 10:14:02 +02:00
|
|
|
ModifierData *md_eval = (ModifierData *)BKE_modifier_get_evaluated(depsgraph, object, md);
|
2019-04-04 14:42:33 +02:00
|
|
|
const int mode = md_eval->mode;
|
|
|
|
|
md_eval->mode |= eModifierMode_Realtime;
|
|
|
|
|
object_force_modifier_update_for_bind(depsgraph, object);
|
|
|
|
|
md_eval->mode = mode;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
ModifierData *modifier_add(
|
2018-04-05 18:20:27 +02:00
|
|
|
ReportList *reports, Main *bmain, Scene *scene, Object *ob, const char *name, int type)
|
2009-07-02 19:41:31 +00:00
|
|
|
{
|
2024-05-08 20:24:10 +02:00
|
|
|
ModifierData *new_md = nullptr;
|
2022-04-18 12:40:23 -05:00
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)type);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-02-12 14:37:16 +11:00
|
|
|
/* Check compatibility of modifier [#25291, #50373]. */
|
2017-01-09 12:24:34 +01:00
|
|
|
if (!BKE_object_support_modifier_type_check(ob, type)) {
|
2012-10-26 17:32:50 +00:00
|
|
|
BKE_reportf(reports, RPT_WARNING, "Modifiers cannot be added to object '%s'", ob->id.name + 2);
|
2022-04-18 12:40:23 -05:00
|
|
|
return nullptr;
|
2010-12-19 01:14:57 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-28 15:42:27 +00:00
|
|
|
if (mti->flags & eModifierTypeFlag_Single) {
|
2022-04-18 12:40:23 -05:00
|
|
|
if (BKE_modifiers_findby_type(ob, (ModifierType)type)) {
|
2012-10-26 17:32:50 +00:00
|
|
|
BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed");
|
2022-04-18 12:40:23 -05:00
|
|
|
return nullptr;
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (type == eModifierType_ParticleSystem) {
|
2009-10-09 09:48:04 +00:00
|
|
|
/* don't need to worry about the new modifier's name, since that is set to the number
|
2018-09-02 18:28:27 +10:00
|
|
|
* of particle systems which shouldn't have too many duplicates
|
|
|
|
|
*/
|
2018-06-13 10:57:10 +02:00
|
|
|
new_md = object_add_particle_system(bmain, scene, ob, name);
|
2009-07-09 19:49:04 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2009-10-09 09:48:04 +00:00
|
|
|
/* get new modifier data to add */
|
2020-05-08 10:14:02 +02:00
|
|
|
new_md = BKE_modifier_new(type);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-05-08 20:24:10 +02:00
|
|
|
ModifierData *next_md = nullptr;
|
|
|
|
|
LISTBASE_FOREACH_BACKWARD (ModifierData *, md, &ob->modifiers) {
|
|
|
|
|
if (md->flag & eModifierFlag_PinLast) {
|
|
|
|
|
next_md = md;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-28 15:42:27 +00:00
|
|
|
if (mti->flags & eModifierTypeFlag_RequiresOriginalData) {
|
2024-05-08 20:24:10 +02:00
|
|
|
next_md = static_cast<ModifierData *>(ob->modifiers.first);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-05-08 20:24:10 +02:00
|
|
|
while (next_md && BKE_modifier_get_info((ModifierType)next_md->type)->type ==
|
|
|
|
|
ModifierTypeType::OnlyDeform)
|
2024-01-02 18:12:54 +01:00
|
|
|
{
|
2024-05-08 20:24:10 +02:00
|
|
|
if (next_md->next && (next_md->next->flag & eModifierFlag_PinLast) != 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
next_md = next_md->next;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
}
|
2024-05-08 20:24:10 +02:00
|
|
|
BLI_insertlinkbefore(&ob->modifiers, next_md, new_md);
|
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(*ob, *new_md);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-20 19:49:27 +00:00
|
|
|
if (name) {
|
2023-05-09 12:50:37 +10:00
|
|
|
STRNCPY_UTF8(new_md->name, name);
|
2012-05-20 19:49:27 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-10-09 09:48:04 +00:00
|
|
|
/* make sure modifier data has unique name */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-05-08 10:14:02 +02:00
|
|
|
BKE_modifier_unique_name(&ob->modifiers, new_md);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-09 19:49:04 +00:00
|
|
|
/* special cases */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (type == eModifierType_Softbody) {
|
|
|
|
|
if (!ob->soft) {
|
2021-05-11 15:59:55 +10:00
|
|
|
ob->soft = sbNew();
|
2012-04-28 15:42:27 +00:00
|
|
|
ob->softflag |= OB_SB_GOAL | OB_SB_EDGES;
|
2009-07-09 19:49:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
2012-03-24 06:38:07 +00:00
|
|
|
else if (type == eModifierType_Collision) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!ob->pd) {
|
2018-12-02 14:14:51 +11:00
|
|
|
ob->pd = BKE_partdeflect_new(0);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-28 15:42:27 +00:00
|
|
|
ob->pd->deflect = 1;
|
2009-07-09 19:49:04 +00:00
|
|
|
}
|
2015-05-12 13:29:00 +05:00
|
|
|
else if (type == eModifierType_Surface) {
|
|
|
|
|
/* pass */
|
|
|
|
|
}
|
2012-05-10 20:34:35 +00:00
|
|
|
else if (type == eModifierType_Multires) {
|
2010-09-26 18:29:54 +00:00
|
|
|
/* set totlvl from existing MDISPS layer if object already had it */
|
2010-09-27 09:43:21 +00:00
|
|
|
multiresModifier_set_levels_from_disps((MultiresModifierData *)new_md, ob);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-04-05 18:20:27 +02:00
|
|
|
if (ob->mode & OB_MODE_SCULPT) {
|
2012-10-01 05:19:57 +00:00
|
|
|
/* ensure that grid paint mask layer is created */
|
2022-10-16 13:24:26 -07:00
|
|
|
BKE_sculpt_mask_layers_ensure(nullptr, nullptr, ob, (MultiresModifierData *)new_md);
|
2012-10-01 05:19:57 +00:00
|
|
|
}
|
2012-05-10 20:34:35 +00:00
|
|
|
}
|
2012-05-22 16:24:09 +00:00
|
|
|
else if (type == eModifierType_Skin) {
|
2012-05-22 15:29:37 +00:00
|
|
|
/* ensure skin-node customdata exists */
|
2022-04-18 12:40:23 -05:00
|
|
|
BKE_mesh_ensure_skin_customdata(static_cast<Mesh *>(ob->data));
|
2012-05-22 15:29:37 +00:00
|
|
|
}
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-12-02 13:35:07 +01:00
|
|
|
BKE_object_modifier_set_active(ob, new_md);
|
|
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2017-06-08 10:14:53 +02:00
|
|
|
DEG_relations_tag_update(bmain);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-11-22 12:28:38 +00:00
|
|
|
return new_md;
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
/* Return true if the object has a modifier of type 'type' other than
|
2018-09-02 18:28:27 +10:00
|
|
|
* the modifier pointed to be 'exclude', otherwise returns false. */
|
2014-02-01 01:45:09 +11:00
|
|
|
static bool object_has_modifier(const Object *ob, const ModifierData *exclude, ModifierType type)
|
2012-06-24 20:18:32 +00:00
|
|
|
{
|
2020-10-09 15:08:01 -05:00
|
|
|
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if ((md != exclude) && (md->type == type)) {
|
2014-04-01 11:34:00 +11:00
|
|
|
return true;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2012-06-24 20:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
return false;
|
2012-06-24 20:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
bool iter_other(Main *bmain,
|
|
|
|
|
Object *orig_ob,
|
|
|
|
|
const bool include_orig,
|
|
|
|
|
bool (*callback)(Object *ob, void *callback_data),
|
|
|
|
|
void *callback_data)
|
2012-06-24 20:18:32 +00:00
|
|
|
{
|
2022-04-18 12:40:23 -05:00
|
|
|
ID *ob_data_id = static_cast<ID *>(orig_ob->data);
|
2012-06-24 20:18:32 +00:00
|
|
|
int users = ob_data_id->us;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-08-07 12:12:17 +02:00
|
|
|
if (ob_data_id->flag & ID_FLAG_FAKEUSER) {
|
2012-06-24 20:18:32 +00:00
|
|
|
users--;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-06-24 20:18:32 +00:00
|
|
|
/* First check that the object's data has multiple users */
|
|
|
|
|
if (users > 1) {
|
|
|
|
|
Object *ob;
|
|
|
|
|
int totfound = include_orig ? 0 : 1;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
for (ob = static_cast<Object *>(bmain->objects.first); ob && totfound < users;
|
|
|
|
|
ob = reinterpret_cast<Object *>(ob->id.next))
|
|
|
|
|
{
|
2012-06-24 20:18:32 +00:00
|
|
|
if (((ob != orig_ob) || include_orig) && (ob->data == orig_ob->data)) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (callback(ob, callback_data)) {
|
2014-04-01 11:34:00 +11:00
|
|
|
return true;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-06-24 20:18:32 +00:00
|
|
|
totfound++;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-06-24 20:18:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (include_orig) {
|
2018-04-05 18:20:27 +02:00
|
|
|
return callback(orig_ob, callback_data);
|
2012-06-24 20:18:32 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
return false;
|
2012-06-24 20:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
2018-04-05 18:20:27 +02:00
|
|
|
static bool object_has_modifier_cb(Object *ob, void *data)
|
2012-06-24 20:18:32 +00:00
|
|
|
{
|
2012-07-29 00:20:28 +00:00
|
|
|
ModifierType type = *((ModifierType *)data);
|
2012-06-24 20:18:32 +00:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
return object_has_modifier(ob, nullptr, type);
|
2012-06-24 20:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
bool multires_update_totlevels(Object *ob, void *totlevel_v)
|
2012-06-24 20:18:32 +00:00
|
|
|
{
|
2015-06-03 13:40:39 +10:00
|
|
|
int totlevel = *((char *)totlevel_v);
|
2012-06-24 20:18:32 +00:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
2012-06-24 20:18:32 +00:00
|
|
|
if (md->type == eModifierType_Multires) {
|
2018-04-05 18:20:27 +02:00
|
|
|
multires_set_tot_level(ob, (MultiresModifierData *)md, totlevel);
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2012-06-24 20:18:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
2014-04-01 11:34:00 +11:00
|
|
|
return false;
|
2012-06-24 20:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
/* Return true if no modifier of type 'type' other than 'exclude' */
|
2014-02-01 01:45:09 +11:00
|
|
|
static bool object_modifier_safe_to_delete(Main *bmain,
|
|
|
|
|
Object *ob,
|
2018-07-31 10:22:19 +02:00
|
|
|
ModifierData *exclude,
|
|
|
|
|
ModifierType type)
|
2012-06-24 20:18:32 +00:00
|
|
|
{
|
|
|
|
|
return (!object_has_modifier(ob, exclude, type) &&
|
2024-03-28 01:30:38 +01:00
|
|
|
!iter_other(bmain, ob, false, object_has_modifier_cb, &type));
|
2012-06-24 20:18:32 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-07-09 11:44:11 +02:00
|
|
|
static bool object_modifier_remove(
|
|
|
|
|
Main *bmain, Scene *scene, Object *ob, ModifierData *md, bool *r_sort_depsgraph)
|
2009-05-21 15:34:09 +00:00
|
|
|
{
|
|
|
|
|
/* It seems on rapid delete it is possible to
|
2018-09-02 18:28:27 +10:00
|
|
|
* get called twice on same modifier, so make
|
|
|
|
|
* sure it is in list. */
|
2012-08-18 19:01:37 +00:00
|
|
|
if (BLI_findindex(&ob->modifiers, md) == -1) {
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2012-08-18 16:16:13 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
/* special cases */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (md->type == eModifierType_ParticleSystem) {
|
2021-08-24 18:38:28 +02:00
|
|
|
object_remove_particle_system(bmain, scene, ob, ((ParticleSystemModifierData *)md)->psys);
|
2020-07-09 11:44:11 +02:00
|
|
|
return true;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
|
|
|
|
|
if (md->type == eModifierType_Softbody) {
|
2012-03-24 06:38:07 +00:00
|
|
|
if (ob->soft) {
|
2018-07-04 11:21:31 +02:00
|
|
|
sbFree(ob);
|
|
|
|
|
ob->softflag = 0; /* TODO(Sybren): this should probably be moved into sbFree() */
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-03-24 06:38:07 +00:00
|
|
|
else if (md->type == eModifierType_Collision) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ob->pd) {
|
2012-04-28 15:42:27 +00:00
|
|
|
ob->pd->deflect = 0;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-02-01 01:45:09 +11:00
|
|
|
*r_sort_depsgraph = true;
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
2012-03-24 06:38:07 +00:00
|
|
|
else if (md->type == eModifierType_Surface) {
|
2014-02-01 01:45:09 +11:00
|
|
|
*r_sort_depsgraph = true;
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
2012-03-24 06:38:07 +00:00
|
|
|
else if (md->type == eModifierType_Multires) {
|
2012-06-24 20:18:32 +00:00
|
|
|
/* Delete MDisps layer if not used by another multires modifier */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (object_modifier_safe_to_delete(bmain, ob, md, eModifierType_Multires)) {
|
2022-04-18 12:40:23 -05:00
|
|
|
multires_customdata_delete(static_cast<Mesh *>(ob->data));
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-12-10 17:37:04 +00:00
|
|
|
}
|
2012-05-26 21:11:23 +00:00
|
|
|
else if (md->type == eModifierType_Skin) {
|
2012-06-24 20:18:32 +00:00
|
|
|
/* Delete MVertSkin layer if not used by another skin modifier */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (object_modifier_safe_to_delete(bmain, ob, md, eModifierType_Skin)) {
|
2012-05-26 21:11:23 +00:00
|
|
|
modifier_skin_customdata_delete(ob);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2012-05-26 21:11:23 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (ELEM(md->type, eModifierType_Softbody, eModifierType_Cloth) &&
|
2018-07-31 20:11:55 +10:00
|
|
|
BLI_listbase_is_empty(&ob->particlesystem))
|
|
|
|
|
{
|
2010-10-19 10:17:19 +00:00
|
|
|
ob->mode &= ~OB_MODE_PARTICLE_EDIT;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-12-02 13:35:07 +01:00
|
|
|
BKE_modifier_remove_from_list(ob, md);
|
2020-05-08 10:14:02 +02:00
|
|
|
BKE_modifier_free(md);
|
2015-11-11 01:56:39 +11:00
|
|
|
BKE_object_free_derived_caches(ob);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-01-08 23:09:31 -06:00
|
|
|
return true;
|
2011-11-23 16:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
bool modifier_remove(ReportList *reports, Main *bmain, Scene *scene, Object *ob, ModifierData *md)
|
2011-11-23 16:12:11 +00:00
|
|
|
{
|
2014-02-01 01:45:09 +11:00
|
|
|
bool sort_depsgraph = false;
|
2011-11-23 16:12:11 +00:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
bool ok = object_modifier_remove(bmain, scene, ob, md, &sort_depsgraph);
|
2011-11-23 16:12:11 +00:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!ok) {
|
2012-08-18 18:54:05 +00:00
|
|
|
BKE_reportf(reports, RPT_ERROR, "Modifier '%s' not in object '%s'", md->name, ob->id.name);
|
2020-09-02 19:10:18 +02:00
|
|
|
return false;
|
2011-11-23 16:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2017-06-08 10:14:53 +02:00
|
|
|
DEG_relations_tag_update(bmain);
|
2010-09-03 07:50:22 +00:00
|
|
|
|
2020-09-02 19:10:18 +02:00
|
|
|
return true;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-30 15:46:01 -04:00
|
|
|
void modifiers_clear(Main *bmain, Scene *scene, Object *ob)
|
2011-11-23 16:12:11 +00:00
|
|
|
{
|
2022-04-18 12:40:23 -05:00
|
|
|
ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first);
|
2014-02-01 01:45:09 +11:00
|
|
|
bool sort_depsgraph = false;
|
2011-11-23 16:12:11 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!md) {
|
2011-11-23 16:44:33 +00:00
|
|
|
return;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2011-11-23 16:12:11 +00:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
while (md) {
|
2020-10-09 15:08:01 -05:00
|
|
|
ModifierData *next_md = md->next;
|
2011-11-23 16:12:11 +00:00
|
|
|
|
2020-07-09 11:44:11 +02:00
|
|
|
object_modifier_remove(bmain, scene, ob, md, &sort_depsgraph);
|
2011-11-23 16:12:11 +00:00
|
|
|
|
2012-04-28 15:42:27 +00:00
|
|
|
md = next_md;
|
2011-11-23 16:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2017-06-08 10:14:53 +02:00
|
|
|
DEG_relations_tag_update(bmain);
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-10 18:25:58 +02:00
|
|
|
static bool object_modifier_check_move_before(ReportList *reports,
|
|
|
|
|
eReportType error_type,
|
|
|
|
|
ModifierData *md,
|
|
|
|
|
ModifierData *md_prev)
|
2009-05-21 15:34:09 +00:00
|
|
|
{
|
2023-01-10 18:25:58 +02:00
|
|
|
if (md_prev) {
|
2024-05-08 20:24:10 +02:00
|
|
|
if (md->flag & eModifierFlag_PinLast && !(md_prev->flag & eModifierFlag_PinLast)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-04-18 12:40:23 -05:00
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
2009-05-21 15:34:09 +00:00
|
|
|
|
2023-11-14 10:03:56 +01:00
|
|
|
if (mti->type != ModifierTypeType::OnlyDeform) {
|
2023-01-10 18:25:58 +02:00
|
|
|
const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md_prev->type);
|
2009-05-21 15:34:09 +00:00
|
|
|
|
2012-04-28 15:42:27 +00:00
|
|
|
if (nmti->flags & eModifierTypeFlag_RequiresOriginalData) {
|
2023-01-10 18:25:58 +02:00
|
|
|
BKE_report(reports, error_type, "Cannot move above a modifier requiring original data");
|
2020-06-05 10:41:03 -04:00
|
|
|
return false;
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
2020-06-05 10:41:03 -04:00
|
|
|
else {
|
2023-01-10 18:25:58 +02:00
|
|
|
BKE_report(reports, error_type, "Cannot move modifier beyond the start of the list");
|
2020-06-05 10:41:03 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2009-05-21 15:34:09 +00:00
|
|
|
|
2020-06-05 10:41:03 -04:00
|
|
|
return true;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
bool modifier_move_up(ReportList *reports, eReportType error_type, Object *ob, ModifierData *md)
|
2023-01-10 18:25:58 +02:00
|
|
|
{
|
|
|
|
|
if (object_modifier_check_move_before(reports, error_type, md, md->prev)) {
|
|
|
|
|
BLI_listbase_swaplinks(&ob->modifiers, md, md->prev);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool object_modifier_check_move_after(ReportList *reports,
|
|
|
|
|
eReportType error_type,
|
|
|
|
|
ModifierData *md,
|
|
|
|
|
ModifierData *md_next)
|
2009-05-21 15:34:09 +00:00
|
|
|
{
|
2023-01-10 18:25:58 +02:00
|
|
|
if (md_next) {
|
2024-05-08 20:24:10 +02:00
|
|
|
if (md_next->flag & eModifierFlag_PinLast && !(md->flag & eModifierFlag_PinLast)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-04-18 12:40:23 -05:00
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
2009-05-21 15:34:09 +00:00
|
|
|
|
2012-04-28 15:42:27 +00:00
|
|
|
if (mti->flags & eModifierTypeFlag_RequiresOriginalData) {
|
2023-01-10 18:25:58 +02:00
|
|
|
const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md_next->type);
|
2009-05-21 15:34:09 +00:00
|
|
|
|
2023-11-14 10:03:56 +01:00
|
|
|
if (nmti->type != ModifierTypeType::OnlyDeform) {
|
2023-01-10 18:25:58 +02:00
|
|
|
BKE_report(reports, error_type, "Cannot move beyond a non-deforming modifier");
|
2020-06-05 10:41:03 -04:00
|
|
|
return false;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-05 10:41:03 -04:00
|
|
|
else {
|
2023-01-10 18:25:58 +02:00
|
|
|
BKE_report(reports, error_type, "Cannot move modifier beyond the end of the list");
|
2020-06-05 10:41:03 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2009-05-21 15:34:09 +00:00
|
|
|
|
2020-06-05 10:41:03 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
bool modifier_move_down(ReportList *reports, eReportType error_type, Object *ob, ModifierData *md)
|
2023-01-10 18:25:58 +02:00
|
|
|
{
|
|
|
|
|
if (object_modifier_check_move_after(reports, error_type, md, md->next)) {
|
|
|
|
|
BLI_listbase_swaplinks(&ob->modifiers, md, md->next);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
bool modifier_move_to_index(ReportList *reports,
|
|
|
|
|
eReportType error_type,
|
|
|
|
|
Object *ob,
|
|
|
|
|
ModifierData *md,
|
|
|
|
|
const int index,
|
|
|
|
|
bool allow_partial)
|
2020-06-05 10:41:03 -04:00
|
|
|
{
|
2022-04-18 12:40:23 -05:00
|
|
|
BLI_assert(md != nullptr);
|
2023-01-10 18:25:58 +02:00
|
|
|
|
|
|
|
|
if (index < 0 || index >= BLI_listbase_count(&ob->modifiers)) {
|
|
|
|
|
BKE_report(reports, error_type, "Cannot move modifier beyond the end of the stack");
|
2020-06-05 10:41:03 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int md_index = BLI_findindex(&ob->modifiers, md);
|
|
|
|
|
BLI_assert(md_index != -1);
|
2023-01-10 18:25:58 +02:00
|
|
|
|
2020-06-05 10:41:03 -04:00
|
|
|
if (md_index < index) {
|
|
|
|
|
/* Move modifier down in list. */
|
2023-01-10 18:25:58 +02:00
|
|
|
ModifierData *md_target = md;
|
|
|
|
|
|
|
|
|
|
for (; md_index < index; md_index++, md_target = md_target->next) {
|
|
|
|
|
if (!object_modifier_check_move_after(reports, error_type, md, md_target->next)) {
|
|
|
|
|
if (!allow_partial || md == md_target) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-05 10:41:03 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-10 18:25:58 +02:00
|
|
|
|
|
|
|
|
BLI_assert(md != md_target && md_target);
|
|
|
|
|
|
|
|
|
|
BLI_remlink(&ob->modifiers, md);
|
|
|
|
|
BLI_insertlinkafter(&ob->modifiers, md_target, md);
|
2020-06-05 10:41:03 -04:00
|
|
|
}
|
2023-01-10 18:25:58 +02:00
|
|
|
else if (md_index > index) {
|
2020-06-05 10:41:03 -04:00
|
|
|
/* Move modifier up in list. */
|
2023-01-10 18:25:58 +02:00
|
|
|
ModifierData *md_target = md;
|
|
|
|
|
|
|
|
|
|
for (; md_index > index; md_index--, md_target = md_target->prev) {
|
|
|
|
|
if (!object_modifier_check_move_before(reports, error_type, md, md_target->prev)) {
|
|
|
|
|
if (!allow_partial || md == md_target) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-05 10:41:03 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-10 18:25:58 +02:00
|
|
|
|
|
|
|
|
BLI_assert(md != md_target && md_target);
|
|
|
|
|
|
|
|
|
|
BLI_remlink(&ob->modifiers, md);
|
|
|
|
|
BLI_insertlinkbefore(&ob->modifiers, md_target, md);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return true;
|
2020-06-05 10:41:03 -04:00
|
|
|
}
|
|
|
|
|
|
2022-08-15 11:54:21 +02:00
|
|
|
/* NOTE: Dependency graph only uses modifier nodes for visibility updates, and exact order of
|
|
|
|
|
* modifier nodes in the graph does not matter. */
|
|
|
|
|
|
2020-09-15 15:01:49 -06:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
2020-06-05 10:41:03 -04:00
|
|
|
return true;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
void modifier_link(bContext *C, Object *ob_dst, Object *ob_src)
|
2020-09-15 15:14:32 -06:00
|
|
|
{
|
|
|
|
|
BKE_object_link_modifiers(ob_dst, ob_src);
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob_dst);
|
|
|
|
|
DEG_id_tag_update(&ob_dst->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
|
2020-09-19 16:27:56 -06:00
|
|
|
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
|
DEG_relations_tag_update(bmain);
|
2020-09-15 15:14:32 -06:00
|
|
|
}
|
|
|
|
|
|
2024-04-30 15:51:05 -04:00
|
|
|
bool modifier_copy_to_object(Main *bmain,
|
|
|
|
|
const Scene *scene,
|
|
|
|
|
const Object *ob_src,
|
|
|
|
|
const ModifierData *md,
|
|
|
|
|
Object *ob_dst,
|
|
|
|
|
ReportList *reports)
|
2020-09-15 15:14:32 -06:00
|
|
|
{
|
2024-04-30 15:51:05 -04:00
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
2020-09-19 16:27:56 -06:00
|
|
|
|
2024-04-30 15:51:05 -04:00
|
|
|
BLI_assert(ob_src != ob_dst);
|
|
|
|
|
|
|
|
|
|
/* Checked in #BKE_object_copy_modifier, but check here too so we can give a better message. */
|
|
|
|
|
if (!BKE_object_support_modifier_type_check(ob_dst, md->type)) {
|
|
|
|
|
BKE_reportf(reports,
|
|
|
|
|
RPT_WARNING,
|
|
|
|
|
"Object '%s' does not support %s modifiers",
|
|
|
|
|
ob_dst->id.name + 2,
|
|
|
|
|
RPT_(mti->name));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mti->flags & eModifierTypeFlag_Single) {
|
|
|
|
|
if (BKE_modifiers_findby_type(ob_dst, (ModifierType)md->type)) {
|
|
|
|
|
BKE_reportf(reports,
|
|
|
|
|
RPT_WARNING,
|
|
|
|
|
"Modifier can only be added once to object '%s'",
|
|
|
|
|
ob_dst->id.name + 2);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!BKE_object_copy_modifier(bmain, scene, ob_dst, ob_src, md)) {
|
|
|
|
|
BKE_reportf(reports,
|
|
|
|
|
RPT_ERROR,
|
|
|
|
|
"Copying modifier '%s' to object '%s' failed",
|
|
|
|
|
md->name,
|
|
|
|
|
ob_dst->id.name + 2);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 20:55:21 +02:00
|
|
|
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob_dst);
|
2024-04-30 15:51:05 -04:00
|
|
|
DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
|
2020-09-19 16:27:56 -06:00
|
|
|
DEG_relations_tag_update(bmain);
|
2024-04-30 15:51:05 -04:00
|
|
|
return true;
|
2020-09-15 15:14:32 -06:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
bool convert_psys_to_mesh(ReportList * /*reports*/,
|
|
|
|
|
Main *bmain,
|
|
|
|
|
Depsgraph *depsgraph,
|
|
|
|
|
Scene *scene,
|
|
|
|
|
ViewLayer *view_layer,
|
|
|
|
|
Object *ob,
|
|
|
|
|
ModifierData *md)
|
2009-05-21 15:34:09 +00:00
|
|
|
{
|
2020-10-09 15:08:01 -05:00
|
|
|
int cvert = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (md->type != eModifierType_ParticleSystem) {
|
2020-09-02 19:10:18 +02:00
|
|
|
return false;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
|
2020-09-02 19:10:18 +02:00
|
|
|
return false;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
ParticleSystem *psys_orig = ((ParticleSystemModifierData *)md)->psys;
|
|
|
|
|
ParticleSettings *part = psys_orig->part;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-01-04 14:28:47 +01:00
|
|
|
if (part->ren_as != PART_DRAW_PATH) {
|
2020-09-02 19:10:18 +02:00
|
|
|
return false;
|
2019-01-04 14:28:47 +01:00
|
|
|
}
|
2020-10-09 15:08:01 -05:00
|
|
|
ParticleSystem *psys_eval = psys_eval_get(depsgraph, ob, psys_orig);
|
2022-04-18 12:40:23 -05:00
|
|
|
if (psys_eval->pathcache == nullptr) {
|
2020-09-02 19:10:18 +02:00
|
|
|
return false;
|
2019-01-04 14:28:47 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-03-28 12:29:47 +11:00
|
|
|
int part_num = psys_eval->totcached;
|
|
|
|
|
int child_num = psys_eval->totchildcache;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-03-28 12:29:47 +11:00
|
|
|
if (child_num && (part->draw & PART_DRAW_PARENT) == 0) {
|
|
|
|
|
part_num = 0;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-01-26 11:15:45 +00:00
|
|
|
/* count */
|
2022-03-28 12:29:47 +11:00
|
|
|
int verts_num = 0, edges_num = 0;
|
2020-10-09 15:08:01 -05:00
|
|
|
ParticleCacheKey **cache = psys_eval->pathcache;
|
2022-03-28 12:29:47 +11:00
|
|
|
for (int a = 0; a < part_num; a++) {
|
2020-10-09 15:08:01 -05:00
|
|
|
ParticleCacheKey *key = cache[a];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-01-13 17:24:20 +01:00
|
|
|
if (key->segments > 0) {
|
2022-03-28 12:29:47 +11:00
|
|
|
verts_num += key->segments + 1;
|
|
|
|
|
edges_num += key->segments;
|
2010-01-26 11:15:45 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2019-01-04 14:28:47 +01:00
|
|
|
cache = psys_eval->childcache;
|
2022-03-28 12:29:47 +11:00
|
|
|
for (int a = 0; a < child_num; a++) {
|
2020-10-09 15:08:01 -05:00
|
|
|
ParticleCacheKey *key = cache[a];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-01-13 17:24:20 +01:00
|
|
|
if (key->segments > 0) {
|
2022-03-28 12:29:47 +11:00
|
|
|
verts_num += key->segments + 1;
|
|
|
|
|
edges_num += key->segments;
|
2010-01-26 11:15:45 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-28 12:29:47 +11:00
|
|
|
if (verts_num == 0) {
|
2020-09-02 19:10:18 +02:00
|
|
|
return false;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-01-26 11:15:45 +00:00
|
|
|
/* add new mesh */
|
2022-09-14 21:30:20 +02:00
|
|
|
Object *obn = BKE_object_add(bmain, scene, view_layer, OB_MESH, nullptr);
|
2023-12-08 16:40:06 -05:00
|
|
|
Mesh *mesh = static_cast<Mesh *>(obn->data);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-20 02:21:48 +01:00
|
|
|
mesh->verts_num = verts_num;
|
|
|
|
|
mesh->edges_num = edges_num;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Mesh: Move edges to a generic attribute
Implements #95966, as the final step of #95965.
This commit changes the storage of mesh edge vertex indices from the
`MEdge` type to the generic `int2` attribute type. This follows the
general design for geometry and the attribute system, where the data
storage type and the usage semantics are separated.
The main benefit of the change is reduced memory usage-- the
requirements of storing mesh edges is reduced by 1/3. For example,
this saves 8MB on a 1 million vertex grid. This also gives performance
benefits to any memory-bound mesh processing algorithm that uses edges.
Another benefit is that all of the edge's vertex indices are
contiguous. In a few cases, it's helpful to process all of them as
`Span<int>` rather than `Span<int2>`. Similarly, the type is more
likely to match a generic format used by a library, or code that
shouldn't know about specific Blender `Mesh` types.
Various Notes:
- The `.edge_verts` name is used to reflect a mapping between domains,
similar to `.corner_verts`, etc. The period means that it the data
shouldn't change arbitrarily by the user or procedural operations.
- `edge[0]` is now used instead of `edge.v1`
- Signed integers are used instead of unsigned to reduce the mixing
of signed-ness, which can be error prone.
- All of the previously used core mesh data types (`MVert`, `MEdge`,
`MLoop`, `MPoly` are now deprecated. Only generic types are used).
- The `vec2i` DNA type is used in the few C files where necessary.
Pull Request: https://projects.blender.org/blender/blender/pulls/106638
2023-04-17 13:47:41 +02:00
|
|
|
CustomData_add_layer_named(
|
2023-12-08 16:40:06 -05:00
|
|
|
&mesh->vert_data, CD_PROP_FLOAT3, CD_CONSTRUCT, verts_num, "position");
|
|
|
|
|
CustomData_add_layer_named(
|
2023-12-20 02:21:48 +01:00
|
|
|
&mesh->edge_data, CD_PROP_INT32_2D, CD_CONSTRUCT, mesh->edges_num, ".edge_verts");
|
2023-12-08 16:40:06 -05:00
|
|
|
CustomData_add_layer(&mesh->fdata_legacy, CD_MFACE, CD_SET_DEFAULT, 0);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
MutableSpan<float3> positions = mesh->vert_positions_for_write();
|
|
|
|
|
MutableSpan<int2> edges = mesh->edges_for_write();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-08 16:40:06 -05:00
|
|
|
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
Mesh: Move selection flags to generic attributes
Using the attribute name semantics from T97452, this patch moves the
selection status of mesh elements from the `SELECT` of vertices, and
edges, and the `ME_FACE_SEL` of faces to generic boolean attribute
Storing this data as generic attributes can significantly simplify and
improve code, as described in T95965.
The attributes are called `.select_vert`, `.select_edge`, and
`.select_poly`. The `.` prefix means they are "UI attributes",so they
still contain original data edited by users, but they aren't meant to
be accessed procedurally by the user in arbitrary situations. They are
also be hidden in the spreadsheet and the attribute list.
Until 4.0, the attributes are still written to and read from the mesh
in the old way, so neither forward nor backward compatibility are
affected. This means memory requirements will be increased by one byte
per element when selection is used. When the flags are removed
completely, requirements will decrease.
Further notes:
* The `MVert` flag is empty at runtime now, so it can be ignored.
* `BMesh` is unchanged, otherwise the change would be much larger.
* Many tests have slightly different results, since the selection
attribute uses more generic propagation. Previously you couldn't
really rely on edit mode selections being propagated procedurally.
Now it mostly works as expected.
Similar to 2480b55f216c
Ref T95965
Differential Revision: https://developer.blender.org/D15795
2022-09-23 09:38:37 -05:00
|
|
|
bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
|
2023-12-20 13:13:16 -05:00
|
|
|
".select_vert", bke::AttrDomain::Point);
|
Mesh: Move selection flags to generic attributes
Using the attribute name semantics from T97452, this patch moves the
selection status of mesh elements from the `SELECT` of vertices, and
edges, and the `ME_FACE_SEL` of faces to generic boolean attribute
Storing this data as generic attributes can significantly simplify and
improve code, as described in T95965.
The attributes are called `.select_vert`, `.select_edge`, and
`.select_poly`. The `.` prefix means they are "UI attributes",so they
still contain original data edited by users, but they aren't meant to
be accessed procedurally by the user in arbitrary situations. They are
also be hidden in the spreadsheet and the attribute list.
Until 4.0, the attributes are still written to and read from the mesh
in the old way, so neither forward nor backward compatibility are
affected. This means memory requirements will be increased by one byte
per element when selection is used. When the flags are removed
completely, requirements will decrease.
Further notes:
* The `MVert` flag is empty at runtime now, so it can be ignored.
* `BMesh` is unchanged, otherwise the change would be much larger.
* Many tests have slightly different results, since the selection
attribute uses more generic propagation. Previously you couldn't
really rely on edit mode selections being propagated procedurally.
Now it mostly works as expected.
Similar to 2480b55f216c
Ref T95965
Differential Revision: https://developer.blender.org/D15795
2022-09-23 09:38:37 -05:00
|
|
|
|
Mesh: Move edges to a generic attribute
Implements #95966, as the final step of #95965.
This commit changes the storage of mesh edge vertex indices from the
`MEdge` type to the generic `int2` attribute type. This follows the
general design for geometry and the attribute system, where the data
storage type and the usage semantics are separated.
The main benefit of the change is reduced memory usage-- the
requirements of storing mesh edges is reduced by 1/3. For example,
this saves 8MB on a 1 million vertex grid. This also gives performance
benefits to any memory-bound mesh processing algorithm that uses edges.
Another benefit is that all of the edge's vertex indices are
contiguous. In a few cases, it's helpful to process all of them as
`Span<int>` rather than `Span<int2>`. Similarly, the type is more
likely to match a generic format used by a library, or code that
shouldn't know about specific Blender `Mesh` types.
Various Notes:
- The `.edge_verts` name is used to reflect a mapping between domains,
similar to `.corner_verts`, etc. The period means that it the data
shouldn't change arbitrarily by the user or procedural operations.
- `edge[0]` is now used instead of `edge.v1`
- Signed integers are used instead of unsigned to reduce the mixing
of signed-ness, which can be error prone.
- All of the previously used core mesh data types (`MVert`, `MEdge`,
`MLoop`, `MPoly` are now deprecated. Only generic types are used).
- The `vec2i` DNA type is used in the few C files where necessary.
Pull Request: https://projects.blender.org/blender/blender/pulls/106638
2023-04-17 13:47:41 +02:00
|
|
|
int edge_index = 0;
|
|
|
|
|
|
2010-01-26 11:15:45 +00:00
|
|
|
/* copy coordinates */
|
Mesh: Move selection flags to generic attributes
Using the attribute name semantics from T97452, this patch moves the
selection status of mesh elements from the `SELECT` of vertices, and
edges, and the `ME_FACE_SEL` of faces to generic boolean attribute
Storing this data as generic attributes can significantly simplify and
improve code, as described in T95965.
The attributes are called `.select_vert`, `.select_edge`, and
`.select_poly`. The `.` prefix means they are "UI attributes",so they
still contain original data edited by users, but they aren't meant to
be accessed procedurally by the user in arbitrary situations. They are
also be hidden in the spreadsheet and the attribute list.
Until 4.0, the attributes are still written to and read from the mesh
in the old way, so neither forward nor backward compatibility are
affected. This means memory requirements will be increased by one byte
per element when selection is used. When the flags are removed
completely, requirements will decrease.
Further notes:
* The `MVert` flag is empty at runtime now, so it can be ignored.
* `BMesh` is unchanged, otherwise the change would be much larger.
* Many tests have slightly different results, since the selection
attribute uses more generic propagation. Previously you couldn't
really rely on edit mode selections being propagated procedurally.
Now it mostly works as expected.
Similar to 2480b55f216c
Ref T95965
Differential Revision: https://developer.blender.org/D15795
2022-09-23 09:38:37 -05:00
|
|
|
int vert_index = 0;
|
2019-01-04 14:28:47 +01:00
|
|
|
cache = psys_eval->pathcache;
|
2022-03-28 12:29:47 +11:00
|
|
|
for (int a = 0; a < part_num; a++) {
|
2020-10-09 15:08:01 -05:00
|
|
|
ParticleCacheKey *key = cache[a];
|
|
|
|
|
int kmax = key->segments;
|
Mesh: Move selection flags to generic attributes
Using the attribute name semantics from T97452, this patch moves the
selection status of mesh elements from the `SELECT` of vertices, and
edges, and the `ME_FACE_SEL` of faces to generic boolean attribute
Storing this data as generic attributes can significantly simplify and
improve code, as described in T95965.
The attributes are called `.select_vert`, `.select_edge`, and
`.select_poly`. The `.` prefix means they are "UI attributes",so they
still contain original data edited by users, but they aren't meant to
be accessed procedurally by the user in arbitrary situations. They are
also be hidden in the spreadsheet and the attribute list.
Until 4.0, the attributes are still written to and read from the mesh
in the old way, so neither forward nor backward compatibility are
affected. This means memory requirements will be increased by one byte
per element when selection is used. When the flags are removed
completely, requirements will decrease.
Further notes:
* The `MVert` flag is empty at runtime now, so it can be ignored.
* `BMesh` is unchanged, otherwise the change would be much larger.
* Many tests have slightly different results, since the selection
attribute uses more generic propagation. Previously you couldn't
really rely on edit mode selections being propagated procedurally.
Now it mostly works as expected.
Similar to 2480b55f216c
Ref T95965
Differential Revision: https://developer.blender.org/D15795
2022-09-23 09:38:37 -05:00
|
|
|
for (int k = 0; k <= kmax; k++, key++, cvert++, vert_index++) {
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
positions[vert_index] = key->co;
|
2012-03-24 06:38:07 +00:00
|
|
|
if (k) {
|
Mesh: Move edges to a generic attribute
Implements #95966, as the final step of #95965.
This commit changes the storage of mesh edge vertex indices from the
`MEdge` type to the generic `int2` attribute type. This follows the
general design for geometry and the attribute system, where the data
storage type and the usage semantics are separated.
The main benefit of the change is reduced memory usage-- the
requirements of storing mesh edges is reduced by 1/3. For example,
this saves 8MB on a 1 million vertex grid. This also gives performance
benefits to any memory-bound mesh processing algorithm that uses edges.
Another benefit is that all of the edge's vertex indices are
contiguous. In a few cases, it's helpful to process all of them as
`Span<int>` rather than `Span<int2>`. Similarly, the type is more
likely to match a generic format used by a library, or code that
shouldn't know about specific Blender `Mesh` types.
Various Notes:
- The `.edge_verts` name is used to reflect a mapping between domains,
similar to `.corner_verts`, etc. The period means that it the data
shouldn't change arbitrarily by the user or procedural operations.
- `edge[0]` is now used instead of `edge.v1`
- Signed integers are used instead of unsigned to reduce the mixing
of signed-ness, which can be error prone.
- All of the previously used core mesh data types (`MVert`, `MEdge`,
`MLoop`, `MPoly` are now deprecated. Only generic types are used).
- The `vec2i` DNA type is used in the few C files where necessary.
Pull Request: https://projects.blender.org/blender/blender/pulls/106638
2023-04-17 13:47:41 +02:00
|
|
|
edges[edge_index] = int2(cvert - 1, cvert);
|
|
|
|
|
edge_index++;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
2010-03-03 19:22:02 +00:00
|
|
|
else {
|
|
|
|
|
/* cheap trick to select the roots */
|
Mesh: Move selection flags to generic attributes
Using the attribute name semantics from T97452, this patch moves the
selection status of mesh elements from the `SELECT` of vertices, and
edges, and the `ME_FACE_SEL` of faces to generic boolean attribute
Storing this data as generic attributes can significantly simplify and
improve code, as described in T95965.
The attributes are called `.select_vert`, `.select_edge`, and
`.select_poly`. The `.` prefix means they are "UI attributes",so they
still contain original data edited by users, but they aren't meant to
be accessed procedurally by the user in arbitrary situations. They are
also be hidden in the spreadsheet and the attribute list.
Until 4.0, the attributes are still written to and read from the mesh
in the old way, so neither forward nor backward compatibility are
affected. This means memory requirements will be increased by one byte
per element when selection is used. When the flags are removed
completely, requirements will decrease.
Further notes:
* The `MVert` flag is empty at runtime now, so it can be ignored.
* `BMesh` is unchanged, otherwise the change would be much larger.
* Many tests have slightly different results, since the selection
attribute uses more generic propagation. Previously you couldn't
really rely on edit mode selections being propagated procedurally.
Now it mostly works as expected.
Similar to 2480b55f216c
Ref T95965
Differential Revision: https://developer.blender.org/D15795
2022-09-23 09:38:37 -05:00
|
|
|
select_vert.span[vert_index] = true;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
2010-01-26 11:15:45 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2019-01-04 14:28:47 +01:00
|
|
|
cache = psys_eval->childcache;
|
2022-03-28 12:29:47 +11:00
|
|
|
for (int a = 0; a < child_num; a++) {
|
2020-10-09 15:08:01 -05:00
|
|
|
ParticleCacheKey *key = cache[a];
|
|
|
|
|
int kmax = key->segments;
|
Mesh: Move selection flags to generic attributes
Using the attribute name semantics from T97452, this patch moves the
selection status of mesh elements from the `SELECT` of vertices, and
edges, and the `ME_FACE_SEL` of faces to generic boolean attribute
Storing this data as generic attributes can significantly simplify and
improve code, as described in T95965.
The attributes are called `.select_vert`, `.select_edge`, and
`.select_poly`. The `.` prefix means they are "UI attributes",so they
still contain original data edited by users, but they aren't meant to
be accessed procedurally by the user in arbitrary situations. They are
also be hidden in the spreadsheet and the attribute list.
Until 4.0, the attributes are still written to and read from the mesh
in the old way, so neither forward nor backward compatibility are
affected. This means memory requirements will be increased by one byte
per element when selection is used. When the flags are removed
completely, requirements will decrease.
Further notes:
* The `MVert` flag is empty at runtime now, so it can be ignored.
* `BMesh` is unchanged, otherwise the change would be much larger.
* Many tests have slightly different results, since the selection
attribute uses more generic propagation. Previously you couldn't
really rely on edit mode selections being propagated procedurally.
Now it mostly works as expected.
Similar to 2480b55f216c
Ref T95965
Differential Revision: https://developer.blender.org/D15795
2022-09-23 09:38:37 -05:00
|
|
|
for (int k = 0; k <= kmax; k++, key++, cvert++, vert_index++) {
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
copy_v3_v3(positions[vert_index], key->co);
|
2012-03-24 06:38:07 +00:00
|
|
|
if (k) {
|
Mesh: Move edges to a generic attribute
Implements #95966, as the final step of #95965.
This commit changes the storage of mesh edge vertex indices from the
`MEdge` type to the generic `int2` attribute type. This follows the
general design for geometry and the attribute system, where the data
storage type and the usage semantics are separated.
The main benefit of the change is reduced memory usage-- the
requirements of storing mesh edges is reduced by 1/3. For example,
this saves 8MB on a 1 million vertex grid. This also gives performance
benefits to any memory-bound mesh processing algorithm that uses edges.
Another benefit is that all of the edge's vertex indices are
contiguous. In a few cases, it's helpful to process all of them as
`Span<int>` rather than `Span<int2>`. Similarly, the type is more
likely to match a generic format used by a library, or code that
shouldn't know about specific Blender `Mesh` types.
Various Notes:
- The `.edge_verts` name is used to reflect a mapping between domains,
similar to `.corner_verts`, etc. The period means that it the data
shouldn't change arbitrarily by the user or procedural operations.
- `edge[0]` is now used instead of `edge.v1`
- Signed integers are used instead of unsigned to reduce the mixing
of signed-ness, which can be error prone.
- All of the previously used core mesh data types (`MVert`, `MEdge`,
`MLoop`, `MPoly` are now deprecated. Only generic types are used).
- The `vec2i` DNA type is used in the few C files where necessary.
Pull Request: https://projects.blender.org/blender/blender/pulls/106638
2023-04-17 13:47:41 +02:00
|
|
|
edges[edge_index] = int2(cvert - 1, cvert);
|
|
|
|
|
edge_index++;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
2010-03-03 19:22:02 +00:00
|
|
|
else {
|
|
|
|
|
/* cheap trick to select the roots */
|
Mesh: Move selection flags to generic attributes
Using the attribute name semantics from T97452, this patch moves the
selection status of mesh elements from the `SELECT` of vertices, and
edges, and the `ME_FACE_SEL` of faces to generic boolean attribute
Storing this data as generic attributes can significantly simplify and
improve code, as described in T95965.
The attributes are called `.select_vert`, `.select_edge`, and
`.select_poly`. The `.` prefix means they are "UI attributes",so they
still contain original data edited by users, but they aren't meant to
be accessed procedurally by the user in arbitrary situations. They are
also be hidden in the spreadsheet and the attribute list.
Until 4.0, the attributes are still written to and read from the mesh
in the old way, so neither forward nor backward compatibility are
affected. This means memory requirements will be increased by one byte
per element when selection is used. When the flags are removed
completely, requirements will decrease.
Further notes:
* The `MVert` flag is empty at runtime now, so it can be ignored.
* `BMesh` is unchanged, otherwise the change would be much larger.
* Many tests have slightly different results, since the selection
attribute uses more generic propagation. Previously you couldn't
really rely on edit mode selections being propagated procedurally.
Now it mostly works as expected.
Similar to 2480b55f216c
Ref T95965
Differential Revision: https://developer.blender.org/D15795
2022-09-23 09:38:37 -05:00
|
|
|
select_vert.span[vert_index] = true;
|
2010-03-03 19:22:02 +00:00
|
|
|
}
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Mesh: Move selection flags to generic attributes
Using the attribute name semantics from T97452, this patch moves the
selection status of mesh elements from the `SELECT` of vertices, and
edges, and the `ME_FACE_SEL` of faces to generic boolean attribute
Storing this data as generic attributes can significantly simplify and
improve code, as described in T95965.
The attributes are called `.select_vert`, `.select_edge`, and
`.select_poly`. The `.` prefix means they are "UI attributes",so they
still contain original data edited by users, but they aren't meant to
be accessed procedurally by the user in arbitrary situations. They are
also be hidden in the spreadsheet and the attribute list.
Until 4.0, the attributes are still written to and read from the mesh
in the old way, so neither forward nor backward compatibility are
affected. This means memory requirements will be increased by one byte
per element when selection is used. When the flags are removed
completely, requirements will decrease.
Further notes:
* The `MVert` flag is empty at runtime now, so it can be ignored.
* `BMesh` is unchanged, otherwise the change would be much larger.
* Many tests have slightly different results, since the selection
attribute uses more generic propagation. Previously you couldn't
really rely on edit mode selections being propagated procedurally.
Now it mostly works as expected.
Similar to 2480b55f216c
Ref T95965
Differential Revision: https://developer.blender.org/D15795
2022-09-23 09:38:37 -05:00
|
|
|
select_vert.finish();
|
|
|
|
|
|
2017-06-08 10:14:53 +02:00
|
|
|
DEG_relations_tag_update(bmain);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-09-02 19:10:18 +02:00
|
|
|
return true;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-14 19:24:34 -06:00
|
|
|
static void add_shapekey_layers(Mesh &mesh_dest, const Mesh &mesh_src)
|
2019-03-26 11:39:11 +01:00
|
|
|
{
|
2022-12-14 19:24:34 -06:00
|
|
|
if (!mesh_src.key) {
|
2022-12-14 19:12:17 -06:00
|
|
|
return;
|
|
|
|
|
}
|
2022-12-14 19:24:34 -06:00
|
|
|
int i;
|
|
|
|
|
LISTBASE_FOREACH_INDEX (const KeyBlock *, kb, &mesh_src.key->block, i) {
|
|
|
|
|
void *array;
|
2023-12-20 02:21:48 +01:00
|
|
|
if (mesh_src.verts_num != kb->totelem) {
|
2022-12-14 19:12:17 -06:00
|
|
|
CLOG_ERROR(&LOG,
|
|
|
|
|
"vertex size mismatch (Mesh '%s':%d != KeyBlock '%s':%d)",
|
2022-12-14 19:24:34 -06:00
|
|
|
mesh_src.id.name + 2,
|
2023-12-20 02:21:48 +01:00
|
|
|
mesh_src.verts_num,
|
2022-12-14 19:12:17 -06:00
|
|
|
kb->name,
|
|
|
|
|
kb->totelem);
|
2023-12-20 02:21:48 +01:00
|
|
|
array = MEM_calloc_arrayN(size_t(mesh_src.verts_num), sizeof(float[3]), __func__);
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
else {
|
2023-12-20 02:21:48 +01:00
|
|
|
array = MEM_malloc_arrayN(size_t(mesh_src.verts_num), sizeof(float[3]), __func__);
|
|
|
|
|
memcpy(array, kb->data, sizeof(float[3]) * size_t(mesh_src.verts_num));
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
|
2024-02-08 16:56:42 +01:00
|
|
|
CustomData_add_layer_with_data(
|
|
|
|
|
&mesh_dest.vert_data, CD_SHAPEKEY, array, mesh_dest.verts_num, nullptr);
|
2023-07-25 21:15:52 +02:00
|
|
|
const int ci = CustomData_get_layer_index_n(&mesh_dest.vert_data, CD_SHAPEKEY, i);
|
2022-12-14 19:12:17 -06:00
|
|
|
|
2023-07-25 21:15:52 +02:00
|
|
|
mesh_dest.vert_data.layers[ci].uid = kb->uid;
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \param use_virtual_modifiers: When enabled, calculate virtual-modifiers before applying
|
|
|
|
|
* `md_eval`. This is supported because virtual-modifiers are not modifiers from a user
|
2023-02-12 14:37:16 +11:00
|
|
|
* perspective, allowing shape keys to be included with the modifier being applied, see: #91923.
|
2022-12-14 19:12:17 -06:00
|
|
|
*/
|
|
|
|
|
static Mesh *create_applied_mesh_for_modifier(Depsgraph *depsgraph,
|
|
|
|
|
Scene *scene,
|
|
|
|
|
Object *ob_eval,
|
|
|
|
|
ModifierData *md_eval,
|
|
|
|
|
const bool use_virtual_modifiers,
|
2022-12-15 14:05:01 -06:00
|
|
|
const bool build_shapekey_layers,
|
|
|
|
|
ReportList *reports)
|
2022-12-14 19:12:17 -06:00
|
|
|
{
|
2023-12-08 16:40:06 -05:00
|
|
|
Mesh *mesh = ob_eval->runtime->data_orig ?
|
|
|
|
|
reinterpret_cast<Mesh *>(ob_eval->runtime->data_orig) :
|
|
|
|
|
reinterpret_cast<Mesh *>(ob_eval->data);
|
2022-12-14 19:24:34 -06:00
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info(ModifierType(md_eval->type));
|
2024-08-01 13:36:43 +02:00
|
|
|
const ModifierEvalContext mectx = {depsgraph, ob_eval, MOD_APPLY_TO_ORIGINAL};
|
2022-12-14 19:12:17 -06:00
|
|
|
|
|
|
|
|
if (!(md_eval->mode & eModifierMode_Realtime)) {
|
2022-12-14 19:24:34 -06:00
|
|
|
return nullptr;
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
|
2023-07-27 12:04:18 +10:00
|
|
|
if (mti->is_disabled && mti->is_disabled(scene, md_eval, false)) {
|
2022-12-14 19:24:34 -06:00
|
|
|
return nullptr;
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
|
2023-12-08 16:40:06 -05:00
|
|
|
if (build_shapekey_layers && mesh->key) {
|
2022-12-14 19:24:34 -06:00
|
|
|
if (KeyBlock *kb = static_cast<KeyBlock *>(
|
2024-01-02 18:12:54 +01:00
|
|
|
BLI_findlink(&mesh->key->block, ob_eval->shapenr - 1)))
|
|
|
|
|
{
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
BKE_keyblock_convert_to_mesh(
|
2023-12-08 16:40:06 -05:00
|
|
|
kb,
|
|
|
|
|
reinterpret_cast<float(*)[3]>(mesh->vert_positions_for_write().data()),
|
2023-12-20 02:21:48 +01:00
|
|
|
mesh->verts_num);
|
2022-12-14 19:24:34 -06:00
|
|
|
}
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
|
2022-12-14 19:24:34 -06:00
|
|
|
Mesh *mesh_temp = reinterpret_cast<Mesh *>(
|
2023-12-08 16:40:06 -05:00
|
|
|
BKE_id_copy_ex(nullptr, &mesh->id, nullptr, LIB_ID_COPY_LOCALIZE));
|
2023-11-14 10:54:57 +01:00
|
|
|
MutableSpan<float3> deformedVerts = mesh_temp->vert_positions_for_write();
|
2022-12-14 19:12:17 -06:00
|
|
|
|
|
|
|
|
if (use_virtual_modifiers) {
|
2023-07-27 12:04:18 +10:00
|
|
|
VirtualModifierData virtual_modifier_data;
|
2022-12-14 19:12:17 -06:00
|
|
|
for (ModifierData *md_eval_virt =
|
2023-07-27 12:04:18 +10:00
|
|
|
BKE_modifiers_get_virtual_modifierlist(ob_eval, &virtual_modifier_data);
|
2022-12-14 19:12:17 -06:00
|
|
|
md_eval_virt && (md_eval_virt != ob_eval->modifiers.first);
|
|
|
|
|
md_eval_virt = md_eval_virt->next)
|
|
|
|
|
{
|
|
|
|
|
if (!BKE_modifier_is_enabled(scene, md_eval_virt, eModifierMode_Realtime)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
/* All virtual modifiers are deform modifiers. */
|
2022-12-14 19:24:34 -06:00
|
|
|
const ModifierTypeInfo *mti_virt = BKE_modifier_get_info(ModifierType(md_eval_virt->type));
|
2023-11-14 10:03:56 +01:00
|
|
|
BLI_assert(mti_virt->type == ModifierTypeType::OnlyDeform);
|
|
|
|
|
if (mti_virt->type != ModifierTypeType::OnlyDeform) {
|
2022-12-14 19:12:17 -06:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-14 10:54:57 +01:00
|
|
|
mti_virt->deform_verts(md_eval_virt, &mectx, mesh_temp, deformedVerts);
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-14 19:24:34 -06:00
|
|
|
Mesh *result = nullptr;
|
2023-11-14 10:03:56 +01:00
|
|
|
if (mti->type == ModifierTypeType::OnlyDeform) {
|
2022-12-14 19:12:17 -06:00
|
|
|
result = mesh_temp;
|
2023-11-14 10:54:57 +01:00
|
|
|
mti->deform_verts(md_eval, &mectx, result, deformedVerts);
|
2023-12-12 15:38:42 -05:00
|
|
|
result->tag_positions_changed();
|
2022-12-14 19:12:17 -06:00
|
|
|
|
|
|
|
|
if (build_shapekey_layers) {
|
2023-12-08 16:40:06 -05:00
|
|
|
add_shapekey_layers(*result, *mesh);
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (build_shapekey_layers) {
|
2023-12-08 16:40:06 -05:00
|
|
|
add_shapekey_layers(*mesh_temp, *mesh);
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
|
2023-07-27 12:04:18 +10:00
|
|
|
if (mti->modify_geometry_set) {
|
2023-08-03 17:09:18 +02:00
|
|
|
bke::GeometrySet geometry_set = bke::GeometrySet::from_mesh(
|
2023-06-15 22:18:28 +02:00
|
|
|
mesh_temp, bke::GeometryOwnershipType::Owned);
|
2023-07-27 12:04:18 +10:00
|
|
|
mti->modify_geometry_set(md_eval, &mectx, &geometry_set);
|
2022-12-15 14:05:01 -06:00
|
|
|
if (!geometry_set.has_mesh()) {
|
|
|
|
|
BKE_report(reports, RPT_ERROR, "Evaluated geometry from modifier does not contain a mesh");
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2023-06-15 22:18:28 +02:00
|
|
|
result = geometry_set.get_component_for_write<bke::MeshComponent>().release();
|
2022-12-15 14:05:01 -06:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-27 12:04:18 +10:00
|
|
|
result = mti->modify_mesh(md_eval, &mectx, mesh_temp);
|
2022-12-15 14:05:01 -06:00
|
|
|
if (mesh_temp != result) {
|
|
|
|
|
BKE_id_free(nullptr, mesh_temp);
|
|
|
|
|
}
|
2022-12-14 19:12:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
2019-03-26 11:39:11 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-01-08 23:09:31 -06:00
|
|
|
static bool modifier_apply_shape(Main *bmain,
|
|
|
|
|
ReportList *reports,
|
|
|
|
|
Depsgraph *depsgraph,
|
|
|
|
|
Scene *scene,
|
|
|
|
|
Object *ob,
|
|
|
|
|
ModifierData *md_eval)
|
2009-05-21 15:34:09 +00:00
|
|
|
{
|
2022-04-18 12:40:23 -05:00
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_eval->type);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-07-27 12:04:18 +10:00
|
|
|
if (mti->is_disabled && mti->is_disabled(scene, md_eval, false)) {
|
2011-06-13 14:11:29 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2011-06-13 14:11:29 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-10-27 13:49:45 +11:00
|
|
|
/* We could investigate using the #CD_ORIGINDEX layer
|
|
|
|
|
* to support other kinds of modifiers besides deforming modifiers.
|
|
|
|
|
* as this is done in many other places, see: #BKE_mesh_foreach_mapped_vert_coords_get.
|
|
|
|
|
*
|
|
|
|
|
* This isn't high priority in practice since most modifiers users
|
|
|
|
|
* want to apply as a shape are deforming modifiers.
|
|
|
|
|
*
|
|
|
|
|
* If a compelling use-case comes up where we want to support other kinds of modifiers
|
|
|
|
|
* we can look into supporting them. */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-28 15:42:27 +00:00
|
|
|
if (ob->type == OB_MESH) {
|
2023-12-08 16:40:06 -05:00
|
|
|
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
|
|
|
|
Key *key = mesh->key;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-11-14 10:03:56 +01:00
|
|
|
if (!BKE_modifier_is_same_topology(md_eval) || mti->type == ModifierTypeType::NonGeometrical) {
|
2012-10-26 17:32:50 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "Only deforming modifiers can be applied to shapes");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-14 19:12:17 -06:00
|
|
|
Mesh *mesh_applied = create_applied_mesh_for_modifier(depsgraph,
|
|
|
|
|
DEG_get_evaluated_scene(depsgraph),
|
|
|
|
|
DEG_get_evaluated_object(depsgraph, ob),
|
|
|
|
|
md_eval,
|
|
|
|
|
true,
|
2022-12-15 14:05:01 -06:00
|
|
|
false,
|
|
|
|
|
reports);
|
2018-06-01 17:05:21 +02:00
|
|
|
if (!mesh_applied) {
|
2009-11-28 04:04:01 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "Modifier is disabled or returned error, skipping apply");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if (key == nullptr) {
|
2023-12-08 16:40:06 -05:00
|
|
|
key = mesh->key = BKE_key_add(bmain, (ID *)mesh);
|
2012-04-28 15:42:27 +00:00
|
|
|
key->type = KEY_RELATIVE;
|
2009-11-28 04:04:01 +00:00
|
|
|
/* if that was the first key block added, then it was the basis.
|
2018-09-02 18:28:27 +10:00
|
|
|
* Initialize it with the mesh, and add another for the modifier */
|
2022-04-18 12:40:23 -05:00
|
|
|
KeyBlock *kb = BKE_keyblock_add(key, nullptr);
|
2023-12-08 16:40:06 -05:00
|
|
|
BKE_keyblock_convert_from_mesh(mesh, key, kb);
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
KeyBlock *kb = BKE_keyblock_add(key, md_eval->name);
|
2023-12-08 16:40:06 -05:00
|
|
|
BKE_mesh_nomain_to_meshkey(mesh_applied, mesh, kb);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
BKE_id_free(nullptr, mesh_applied);
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
2021-01-08 23:09:31 -06:00
|
|
|
return true;
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
static bool meta_data_matches(const std::optional<bke::AttributeMetaData> meta_data,
|
2023-12-20 13:13:16 -05:00
|
|
|
const AttrDomainMask domains,
|
2022-12-26 10:49:21 -05:00
|
|
|
const eCustomDataMask types)
|
|
|
|
|
{
|
|
|
|
|
if (!meta_data) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!(ATTR_DOMAIN_AS_MASK(meta_data->domain) & domains)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!(CD_TYPE_AS_MASK(meta_data->data_type) & types)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-15 14:18:57 -06:00
|
|
|
static void remove_invalid_attribute_strings(Mesh &mesh)
|
|
|
|
|
{
|
|
|
|
|
bke::AttributeAccessor attributes = mesh.attributes();
|
2022-12-26 10:49:21 -05:00
|
|
|
if (!meta_data_matches(attributes.lookup_meta_data(mesh.active_color_attribute),
|
|
|
|
|
ATTR_DOMAIN_MASK_COLOR,
|
|
|
|
|
CD_MASK_COLOR_ALL))
|
|
|
|
|
{
|
|
|
|
|
MEM_SAFE_FREE(mesh.active_color_attribute);
|
|
|
|
|
}
|
|
|
|
|
if (!meta_data_matches(attributes.lookup_meta_data(mesh.default_color_attribute),
|
|
|
|
|
ATTR_DOMAIN_MASK_COLOR,
|
|
|
|
|
CD_MASK_COLOR_ALL))
|
|
|
|
|
{
|
|
|
|
|
MEM_SAFE_FREE(mesh.default_color_attribute);
|
2022-12-15 14:18:57 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-04 11:51:30 +02:00
|
|
|
static void apply_eval_grease_pencil_data(const GreasePencil &src_grease_pencil,
|
|
|
|
|
const int eval_frame,
|
2024-11-08 13:08:58 +01:00
|
|
|
const IndexMask &orig_layers,
|
2024-10-04 11:51:30 +02:00
|
|
|
GreasePencil &orig_grease_pencil)
|
2024-08-13 15:02:25 +02:00
|
|
|
{
|
|
|
|
|
using namespace bke;
|
|
|
|
|
using namespace bke::greasepencil;
|
2024-11-08 13:08:58 +01:00
|
|
|
/* Build a set of pointers to the layers that we want to apply. */
|
|
|
|
|
Set<const Layer *> orig_layers_to_apply;
|
|
|
|
|
orig_layers.foreach_index([&](const int layer_i) {
|
|
|
|
|
const Layer &layer = orig_grease_pencil.layer(layer_i);
|
|
|
|
|
orig_layers_to_apply.add(&layer);
|
|
|
|
|
});
|
2024-08-13 15:02:25 +02:00
|
|
|
|
2024-09-27 13:17:18 +02:00
|
|
|
/* Ensure that the layer names are unique by merging layers with the same name. */
|
2024-10-04 11:51:30 +02:00
|
|
|
const int old_layers_num = src_grease_pencil.layers().size();
|
2024-09-27 13:17:18 +02:00
|
|
|
Vector<Vector<int>> layers_map;
|
|
|
|
|
Map<StringRef, int> new_layer_index_by_name;
|
|
|
|
|
for (const int layer_i : IndexRange(old_layers_num)) {
|
2024-10-04 11:51:30 +02:00
|
|
|
const Layer &layer = src_grease_pencil.layer(layer_i);
|
2024-09-27 13:17:18 +02:00
|
|
|
const int new_layer_index = new_layer_index_by_name.lookup_or_add_cb(
|
|
|
|
|
layer.name(), [&]() { return layers_map.append_and_get_index_as(); });
|
|
|
|
|
layers_map[new_layer_index].append(layer_i);
|
|
|
|
|
}
|
2024-10-04 11:51:30 +02:00
|
|
|
GreasePencil &merged_layers_grease_pencil = *geometry::merge_layers(
|
|
|
|
|
src_grease_pencil, layers_map, {});
|
2024-09-27 13:17:18 +02:00
|
|
|
|
2024-08-13 15:02:25 +02:00
|
|
|
Map<const Layer *, const Layer *> eval_to_orig_layer_map;
|
|
|
|
|
{
|
2024-11-08 08:28:06 +01:00
|
|
|
/* Keep track of the last layer in each group to ensure layers get added to the same groups in
|
|
|
|
|
* the same order as the original. This is better than using the layer cache since it avoids
|
|
|
|
|
* updating the cache every time a new layer is added. */
|
|
|
|
|
Map<const LayerGroup *, TreeNode *> last_node_by_group;
|
2024-11-08 13:08:58 +01:00
|
|
|
/* Set of orig layers that require the drawing on `eval_frame` to be cleared. These are layers
|
|
|
|
|
* that existed in original geometry but were removed during the modifier evaluation. */
|
|
|
|
|
Set<Layer *> orig_layers_to_clear(orig_grease_pencil.layers_for_write());
|
2024-11-08 08:28:06 +01:00
|
|
|
for (const TreeNode *node_eval : merged_layers_grease_pencil.nodes()) {
|
2024-08-13 15:02:25 +02:00
|
|
|
/* Check if the original geometry has a layer with the same name. */
|
2024-11-08 08:28:06 +01:00
|
|
|
TreeNode *node_orig = orig_grease_pencil.find_node_by_name(node_eval->name());
|
|
|
|
|
|
2024-11-08 13:08:58 +01:00
|
|
|
BLI_assert(node_eval != nullptr);
|
2024-11-08 08:28:06 +01:00
|
|
|
if (node_eval->is_layer()) {
|
|
|
|
|
/* If the orig layer isn't valid then a new layer with a unique name will be generated. */
|
|
|
|
|
const bool has_valid_orig_layer = (node_orig != nullptr && node_orig->is_layer());
|
|
|
|
|
if (!has_valid_orig_layer) {
|
|
|
|
|
/* Note: This name might be empty! This has to be resolved at a later stage! */
|
|
|
|
|
Layer &layer_orig = orig_grease_pencil.add_layer(node_eval->name(), true);
|
2024-11-08 13:08:58 +01:00
|
|
|
orig_layers_to_apply.add(&layer_orig);
|
2024-11-08 08:28:06 +01:00
|
|
|
/* Make sure to add a new keyframe with a new drawing. */
|
|
|
|
|
orig_grease_pencil.insert_frame(layer_orig, eval_frame);
|
|
|
|
|
node_orig = &layer_orig.as_node();
|
2024-10-04 11:51:30 +02:00
|
|
|
}
|
2024-08-13 15:02:25 +02:00
|
|
|
BLI_assert(node_orig != nullptr);
|
2024-11-08 08:28:06 +01:00
|
|
|
Layer &layer_orig = node_orig->as_layer();
|
2024-11-08 13:08:58 +01:00
|
|
|
/* This layer has a matching evaluated layer, so don't clear its keyframe. */
|
|
|
|
|
orig_layers_to_clear.remove(&layer_orig);
|
|
|
|
|
/* Only map layers in `eval_to_orig_layer_map` that we want to apply. */
|
|
|
|
|
if (orig_layers_to_apply.contains(&layer_orig)) {
|
|
|
|
|
/* Copy layer properties to original geometry. */
|
|
|
|
|
const Layer &layer_eval = node_eval->as_layer();
|
|
|
|
|
layer_orig.opacity = layer_eval.opacity;
|
|
|
|
|
layer_orig.set_local_transform(layer_eval.local_transform());
|
|
|
|
|
|
|
|
|
|
/* Add new mapping for layer_eval -> layer_orig*/
|
|
|
|
|
eval_to_orig_layer_map.add_new(&layer_eval, &layer_orig);
|
|
|
|
|
}
|
2024-08-13 15:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-08 08:28:06 +01:00
|
|
|
/* Insert the updated node after the last node in the same group.
|
|
|
|
|
* This keeps the layer order consistent. */
|
|
|
|
|
if (node_orig && node_orig->parent_group()) {
|
|
|
|
|
last_node_by_group.add_or_modify(
|
|
|
|
|
node_orig->parent_group(),
|
|
|
|
|
[&](TreeNode **node_ptr) {
|
|
|
|
|
/* First layer in the group, set the last-layer pointer. */
|
|
|
|
|
*node_ptr = node_orig;
|
|
|
|
|
},
|
|
|
|
|
[&](TreeNode **node_ptr) {
|
|
|
|
|
orig_grease_pencil.move_node_after(*node_orig, **node_ptr);
|
|
|
|
|
*node_ptr = node_orig;
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-08-13 15:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-08 13:08:58 +01:00
|
|
|
/* Clear the keyframe of all the original layers that don't have a matching evaluated layer,
|
|
|
|
|
* e.g. the ones that were "deleted" in the modifier. */
|
|
|
|
|
for (Layer *layer_orig : orig_layers_to_clear) {
|
2024-11-08 08:28:06 +01:00
|
|
|
/* Try inserting a frame. */
|
|
|
|
|
Drawing *drawing_orig = orig_grease_pencil.insert_frame(*layer_orig, eval_frame);
|
|
|
|
|
if (drawing_orig == nullptr) {
|
|
|
|
|
/* If that fails, get the drawing for this frame. */
|
|
|
|
|
drawing_orig = orig_grease_pencil.get_drawing_at(*layer_orig, eval_frame);
|
2024-08-13 15:02:25 +02:00
|
|
|
}
|
2024-11-08 08:28:06 +01:00
|
|
|
/* Clear the existing drawing. */
|
|
|
|
|
drawing_orig->strokes_for_write() = {};
|
|
|
|
|
drawing_orig->tag_topology_changed();
|
2024-08-13 15:02:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update the drawings. */
|
|
|
|
|
VectorSet<Drawing *> all_updated_drawings;
|
|
|
|
|
for (auto [layer_eval, layer_orig] : eval_to_orig_layer_map.items()) {
|
2024-10-04 11:51:30 +02:00
|
|
|
const Drawing *drawing_eval = merged_layers_grease_pencil.get_drawing_at(*layer_eval,
|
|
|
|
|
eval_frame);
|
|
|
|
|
Drawing *drawing_orig = orig_grease_pencil.get_drawing_at(*layer_orig, eval_frame);
|
2024-08-13 15:02:25 +02:00
|
|
|
if (drawing_orig && drawing_eval) {
|
|
|
|
|
/* Write the data to the original drawing. */
|
2024-10-04 11:51:30 +02:00
|
|
|
drawing_orig->strokes_for_write() = std::move(drawing_eval->strokes());
|
|
|
|
|
/* Anonymous attributes shouldn't be available on original geometry. */
|
|
|
|
|
drawing_orig->strokes_for_write().attributes_for_write().remove_anonymous();
|
2024-08-13 15:02:25 +02:00
|
|
|
drawing_orig->tag_topology_changed();
|
|
|
|
|
all_updated_drawings.add_new(drawing_orig);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the original material pointers from the result geometry. */
|
|
|
|
|
VectorSet<Material *> original_materials;
|
2024-10-04 11:51:30 +02:00
|
|
|
const Span<Material *> eval_materials = Span{merged_layers_grease_pencil.material_array,
|
|
|
|
|
merged_layers_grease_pencil.material_array_num};
|
2024-08-13 15:02:25 +02:00
|
|
|
for (Material *eval_material : eval_materials) {
|
|
|
|
|
if (eval_material != nullptr && eval_material->id.orig_id != nullptr) {
|
|
|
|
|
original_materials.add_new(reinterpret_cast<Material *>(eval_material->id.orig_id));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Build material indices mapping. This maps the materials indices on the original geometry to
|
|
|
|
|
* the material indices used in the result geometry. The material indices for the drawings in the
|
|
|
|
|
* result geometry are already correct, but this might not be the case for all drawings in the
|
|
|
|
|
* original geometry (like for drawings that are not visible on the frame that the modifier is
|
|
|
|
|
* being applied on). */
|
2024-10-04 11:51:30 +02:00
|
|
|
Array<int> material_indices_map(orig_grease_pencil.material_array_num);
|
|
|
|
|
for (const int mat_i : IndexRange(orig_grease_pencil.material_array_num)) {
|
|
|
|
|
Material *material = orig_grease_pencil.material_array[mat_i];
|
2024-08-13 15:02:25 +02:00
|
|
|
const int map_index = original_materials.index_of_try(material);
|
|
|
|
|
if (map_index != -1) {
|
|
|
|
|
material_indices_map[mat_i] = map_index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remap material indices for all other drawings. */
|
|
|
|
|
if (!material_indices_map.is_empty() &&
|
|
|
|
|
!array_utils::indices_are_range(material_indices_map,
|
2024-10-04 11:51:30 +02:00
|
|
|
IndexRange(orig_grease_pencil.material_array_num)))
|
2024-08-13 15:02:25 +02:00
|
|
|
{
|
2024-10-04 11:51:30 +02:00
|
|
|
for (GreasePencilDrawingBase *base : orig_grease_pencil.drawings()) {
|
2024-08-13 15:02:25 +02:00
|
|
|
if (base->type != GP_DRAWING) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
|
|
|
|
|
if (all_updated_drawings.contains(&drawing)) {
|
|
|
|
|
/* Skip remapping drawings that already have been updated. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
MutableAttributeAccessor attributes = drawing.strokes_for_write().attributes_for_write();
|
|
|
|
|
if (!attributes.contains("material_index")) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
|
|
|
|
|
"material_index", AttrDomain::Curve);
|
|
|
|
|
for (int &material_index : material_indices.span) {
|
|
|
|
|
if (material_index >= 0 && material_index < material_indices_map.size()) {
|
|
|
|
|
material_index = material_indices_map[material_index];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
material_indices.finish();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert the layer map into an index mapping. */
|
2024-10-04 11:51:30 +02:00
|
|
|
Map<int, int> eval_to_orig_layer_indices_map;
|
|
|
|
|
for (const int layer_eval_i : merged_layers_grease_pencil.layers().index_range()) {
|
|
|
|
|
const Layer *layer_eval = &merged_layers_grease_pencil.layer(layer_eval_i);
|
|
|
|
|
if (eval_to_orig_layer_map.contains(layer_eval)) {
|
|
|
|
|
const Layer *layer_orig = eval_to_orig_layer_map.lookup(layer_eval);
|
|
|
|
|
const int layer_orig_index = *orig_grease_pencil.get_layer_index(*layer_orig);
|
|
|
|
|
eval_to_orig_layer_indices_map.add(layer_eval_i, layer_orig_index);
|
|
|
|
|
}
|
2024-08-13 15:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Propagate layer attributes. */
|
2024-10-04 11:51:30 +02:00
|
|
|
AttributeAccessor src_attributes = merged_layers_grease_pencil.attributes();
|
|
|
|
|
MutableAttributeAccessor dst_attributes = orig_grease_pencil.attributes_for_write();
|
2024-09-26 12:59:00 +02:00
|
|
|
src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
|
2024-10-04 11:51:30 +02:00
|
|
|
/* Anonymous attributes shouldn't be available on original geometry. */
|
|
|
|
|
if (attribute_name_is_anonymous(iter.name)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-09-26 12:59:00 +02:00
|
|
|
if (iter.data_type == CD_PROP_STRING) {
|
|
|
|
|
return;
|
2024-08-13 15:02:25 +02:00
|
|
|
}
|
2024-09-26 12:59:00 +02:00
|
|
|
const GVArraySpan src = *iter.get(AttrDomain::Layer);
|
2024-08-13 15:02:25 +02:00
|
|
|
GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
|
2024-09-26 12:59:00 +02:00
|
|
|
iter.name, AttrDomain::Layer, iter.data_type);
|
2024-08-13 15:02:25 +02:00
|
|
|
if (!dst) {
|
2024-09-26 12:59:00 +02:00
|
|
|
return;
|
2024-08-13 15:02:25 +02:00
|
|
|
}
|
|
|
|
|
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
|
|
|
|
using T = decltype(dummy);
|
2024-10-04 11:51:30 +02:00
|
|
|
Span<T> src_span = src.typed<T>();
|
|
|
|
|
MutableSpan<T> dst_span = dst.span.typed<T>();
|
|
|
|
|
for (const auto [src_i, dst_i] : eval_to_orig_layer_indices_map.items()) {
|
|
|
|
|
dst_span[dst_i] = src_span[src_i];
|
|
|
|
|
}
|
2024-08-13 15:02:25 +02:00
|
|
|
});
|
|
|
|
|
dst.finish();
|
|
|
|
|
});
|
|
|
|
|
|
2024-10-04 11:51:30 +02:00
|
|
|
/* Free temporary grease pencil struct. */
|
|
|
|
|
BKE_id_free(nullptr, &merged_layers_grease_pencil);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool apply_grease_pencil_for_modifier(Depsgraph *depsgraph,
|
|
|
|
|
Object *ob,
|
|
|
|
|
GreasePencil &grease_pencil_orig,
|
|
|
|
|
ModifierData *md_eval)
|
|
|
|
|
{
|
|
|
|
|
using namespace bke;
|
|
|
|
|
using namespace bke::greasepencil;
|
|
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info(ModifierType(md_eval->type));
|
|
|
|
|
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
|
|
|
|
|
GreasePencil *grease_pencil_for_eval = ob_eval->runtime->data_orig ?
|
|
|
|
|
reinterpret_cast<GreasePencil *>(
|
|
|
|
|
ob_eval->runtime->data_orig) :
|
|
|
|
|
&grease_pencil_orig;
|
|
|
|
|
const int eval_frame = int(DEG_get_ctime(depsgraph));
|
|
|
|
|
GreasePencil *grease_pencil_temp = reinterpret_cast<GreasePencil *>(
|
|
|
|
|
BKE_id_copy_ex(nullptr, &grease_pencil_for_eval->id, nullptr, LIB_ID_COPY_LOCALIZE));
|
|
|
|
|
grease_pencil_temp->runtime->eval_frame = eval_frame;
|
|
|
|
|
GeometrySet eval_geometry_set = GeometrySet::from_grease_pencil(grease_pencil_temp,
|
|
|
|
|
GeometryOwnershipType::Owned);
|
|
|
|
|
|
|
|
|
|
ModifierEvalContext mectx = {depsgraph, ob_eval, MOD_APPLY_TO_ORIGINAL};
|
|
|
|
|
mti->modify_geometry_set(md_eval, &mectx, &eval_geometry_set);
|
|
|
|
|
if (!eval_geometry_set.has_grease_pencil()) {
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
GreasePencil &grease_pencil_result =
|
|
|
|
|
*eval_geometry_set.get_component_for_write<GreasePencilComponent>().get_for_write();
|
|
|
|
|
|
|
|
|
|
apply_eval_grease_pencil_data(grease_pencil_result,
|
|
|
|
|
eval_frame,
|
|
|
|
|
grease_pencil_orig.layers().index_range(),
|
|
|
|
|
grease_pencil_orig);
|
|
|
|
|
|
2024-08-13 15:02:25 +02:00
|
|
|
Main *bmain = DEG_get_bmain(depsgraph);
|
2024-10-04 11:51:30 +02:00
|
|
|
/* There might be layers with empty names after evaluation. Make sure to rename them. */
|
|
|
|
|
for (Layer *layer : grease_pencil_orig.layers_for_write()) {
|
|
|
|
|
if (layer->name().is_empty()) {
|
|
|
|
|
grease_pencil_orig.rename_node(*bmain, layer->as_node(), DATA_("Layer"));
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-13 15:02:25 +02:00
|
|
|
BKE_object_material_from_eval_data(bmain, ob, &grease_pencil_result.id);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-04 11:51:30 +02:00
|
|
|
static bool apply_grease_pencil_for_modifier_all_keyframes(Depsgraph *depsgraph,
|
|
|
|
|
Scene *scene,
|
|
|
|
|
Object *ob,
|
|
|
|
|
GreasePencil &grease_pencil_orig,
|
2024-11-04 10:38:46 +01:00
|
|
|
ModifierData *md)
|
2024-10-04 11:51:30 +02:00
|
|
|
{
|
|
|
|
|
using namespace bke;
|
|
|
|
|
using namespace bke::greasepencil;
|
|
|
|
|
Main *bmain = DEG_get_bmain(depsgraph);
|
2024-11-04 10:38:46 +01:00
|
|
|
|
|
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info(ModifierType(md->type));
|
2024-10-04 11:51:30 +02:00
|
|
|
|
|
|
|
|
WM_cursor_wait(true);
|
|
|
|
|
|
|
|
|
|
Map<int, Vector<int>> layer_indices_to_apply_per_frame;
|
|
|
|
|
{
|
|
|
|
|
for (const int layer_i : grease_pencil_orig.layers().index_range()) {
|
|
|
|
|
const Layer &layer = grease_pencil_orig.layer(layer_i);
|
|
|
|
|
for (const auto &[key, value] : layer.frames().items()) {
|
|
|
|
|
if (value.is_end()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
layer_indices_to_apply_per_frame.lookup_or_add(key, {}).append(layer_i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Array<int> sorted_frame_times(layer_indices_to_apply_per_frame.size());
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (const int key : layer_indices_to_apply_per_frame.keys()) {
|
|
|
|
|
sorted_frame_times[i++] = key;
|
|
|
|
|
}
|
|
|
|
|
std::sort(sorted_frame_times.begin(), sorted_frame_times.end());
|
|
|
|
|
|
|
|
|
|
const int prev_frame = int(DEG_get_ctime(depsgraph));
|
|
|
|
|
bool changed = false;
|
|
|
|
|
for (const int eval_frame : sorted_frame_times) {
|
|
|
|
|
const Span<int> layer_indices = layer_indices_to_apply_per_frame.lookup(eval_frame).as_span();
|
|
|
|
|
scene->r.cfra = eval_frame;
|
|
|
|
|
BKE_scene_graph_update_for_newframe(depsgraph);
|
|
|
|
|
|
|
|
|
|
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
|
|
|
|
|
GreasePencil *grease_pencil_for_eval = ob_eval->runtime->data_orig ?
|
|
|
|
|
reinterpret_cast<GreasePencil *>(
|
|
|
|
|
ob_eval->runtime->data_orig) :
|
|
|
|
|
&grease_pencil_orig;
|
|
|
|
|
|
|
|
|
|
GreasePencil *grease_pencil_temp = reinterpret_cast<GreasePencil *>(
|
|
|
|
|
BKE_id_copy_ex(nullptr, &grease_pencil_for_eval->id, nullptr, LIB_ID_COPY_LOCALIZE));
|
|
|
|
|
grease_pencil_temp->runtime->eval_frame = eval_frame;
|
|
|
|
|
GeometrySet eval_geometry_set = GeometrySet::from_grease_pencil(grease_pencil_temp,
|
|
|
|
|
GeometryOwnershipType::Owned);
|
|
|
|
|
|
2024-11-04 10:38:46 +01:00
|
|
|
ModifierData *md_eval = BKE_modifier_get_evaluated(depsgraph, ob, md);
|
2024-10-04 11:51:30 +02:00
|
|
|
ModifierEvalContext mectx = {depsgraph, ob_eval, MOD_APPLY_TO_ORIGINAL};
|
|
|
|
|
mti->modify_geometry_set(md_eval, &mectx, &eval_geometry_set);
|
|
|
|
|
if (!eval_geometry_set.has_grease_pencil()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
GreasePencil &grease_pencil_result =
|
|
|
|
|
*eval_geometry_set.get_component_for_write<GreasePencilComponent>().get_for_write();
|
|
|
|
|
|
|
|
|
|
IndexMaskMemory memory;
|
|
|
|
|
const IndexMask orig_layers_to_apply = IndexMask::from_indices(layer_indices, memory);
|
|
|
|
|
apply_eval_grease_pencil_data(
|
|
|
|
|
grease_pencil_result, eval_frame, orig_layers_to_apply, grease_pencil_orig);
|
|
|
|
|
|
|
|
|
|
BKE_object_material_from_eval_data(bmain, ob, &grease_pencil_result.id);
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scene->r.cfra = prev_frame;
|
|
|
|
|
BKE_scene_graph_update_for_newframe(depsgraph);
|
|
|
|
|
|
|
|
|
|
/* There might be layers with empty names after evaluation. Make sure to rename them. */
|
|
|
|
|
for (Layer *layer : grease_pencil_orig.layers_for_write()) {
|
|
|
|
|
if (layer->name().is_empty()) {
|
|
|
|
|
grease_pencil_orig.rename_node(*bmain, layer->as_node(), DATA_("Layer"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WM_cursor_wait(false);
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool modifier_apply_obdata(ReportList *reports,
|
|
|
|
|
Depsgraph *depsgraph,
|
|
|
|
|
Scene *scene,
|
|
|
|
|
Object *ob,
|
|
|
|
|
ModifierData *md_eval,
|
|
|
|
|
const bool do_all_keyframes)
|
2009-11-28 04:04:01 +00:00
|
|
|
{
|
2022-04-18 12:40:23 -05:00
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_eval->type);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-07-27 12:04:18 +10:00
|
|
|
if (mti->is_disabled && mti->is_disabled(scene, md_eval, false)) {
|
2010-04-13 06:06:49 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2010-04-13 06:06:49 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-28 15:42:27 +00:00
|
|
|
if (ob->type == OB_MESH) {
|
2023-12-08 16:40:06 -05:00
|
|
|
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
2019-04-28 15:41:30 +02:00
|
|
|
MultiresModifierData *mmd = find_multires_modifier_before(scene, md_eval);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-08 16:40:06 -05:00
|
|
|
if (mesh->key && mti->type != ModifierTypeType::NonGeometrical) {
|
2012-10-26 17:32:50 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "Modifier cannot be applied to a mesh with shape keys");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2011-07-25 11:34:09 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-11-28 04:04:01 +00:00
|
|
|
/* Multires: ensure that recent sculpting is applied */
|
2019-04-28 15:41:30 +02:00
|
|
|
if (md_eval->type == eModifierType_Multires) {
|
2019-09-17 17:24:44 +02:00
|
|
|
multires_force_sculpt_rebuild(ob);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-11-14 10:03:56 +01:00
|
|
|
if (mmd && mmd->totlvl && mti->type == ModifierTypeType::OnlyDeform) {
|
2020-03-03 12:35:51 +01:00
|
|
|
if (!multiresModifier_reshapeFromDeformModifier(depsgraph, ob, mmd, md_eval)) {
|
2010-07-05 12:20:49 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "Multires modifier returned error, skipping apply");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2010-07-05 12:20:49 +00:00
|
|
|
}
|
2012-03-24 06:38:07 +00:00
|
|
|
else {
|
2022-12-14 19:12:17 -06:00
|
|
|
Mesh *mesh_applied = create_applied_mesh_for_modifier(
|
2022-05-06 13:33:21 +10:00
|
|
|
depsgraph,
|
2022-12-14 19:12:17 -06:00
|
|
|
DEG_get_evaluated_scene(depsgraph),
|
|
|
|
|
DEG_get_evaluated_object(depsgraph, ob),
|
2022-05-06 13:33:21 +10:00
|
|
|
md_eval,
|
|
|
|
|
/* It's important not to apply virtual modifiers (e.g. shape-keys) because they're kept,
|
2023-02-12 14:37:16 +11:00
|
|
|
* causing them to be applied twice, see: #97758. */
|
2022-05-06 13:33:21 +10:00
|
|
|
false,
|
2022-12-15 14:05:01 -06:00
|
|
|
true,
|
|
|
|
|
reports);
|
2018-06-01 17:05:21 +02:00
|
|
|
if (!mesh_applied) {
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2010-04-13 06:06:49 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-05-20 12:15:57 +02:00
|
|
|
Main *bmain = DEG_get_bmain(depsgraph);
|
|
|
|
|
BKE_object_material_from_eval_data(bmain, ob, &mesh_applied->id);
|
2023-12-08 16:40:06 -05:00
|
|
|
BKE_mesh_nomain_to_mesh(mesh_applied, mesh, ob);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-02-10 18:03:37 +01:00
|
|
|
/* Anonymous attributes shouldn't be available on the applied geometry. */
|
2023-12-08 16:40:06 -05:00
|
|
|
mesh->attributes_for_write().remove_anonymous();
|
2022-02-03 16:52:16 +01:00
|
|
|
|
2022-12-15 14:18:57 -06:00
|
|
|
/* Remove strings referring to attributes if they no longer exist. */
|
2023-12-08 16:40:06 -05:00
|
|
|
remove_invalid_attribute_strings(*mesh);
|
2022-12-15 14:18:57 -06:00
|
|
|
|
2019-04-28 15:41:30 +02:00
|
|
|
if (md_eval->type == eModifierType_Multires) {
|
2023-12-08 16:40:06 -05:00
|
|
|
multires_customdata_delete(mesh);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-11-22 13:44:09 +00:00
|
|
|
}
|
2022-02-18 09:50:29 -06:00
|
|
|
else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
|
2019-03-26 11:39:11 +01:00
|
|
|
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
|
2022-04-18 12:40:23 -05:00
|
|
|
Curve *curve = static_cast<Curve *>(ob->data);
|
|
|
|
|
Curve *curve_eval = static_cast<Curve *>(object_eval->data);
|
2024-08-01 13:36:43 +02:00
|
|
|
ModifierEvalContext mectx = {depsgraph, object_eval, MOD_APPLY_TO_ORIGINAL};
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-11-14 10:03:56 +01:00
|
|
|
if (ELEM(mti->type, ModifierTypeType::Constructive, ModifierTypeType::Nonconstructive)) {
|
2018-08-14 12:06:04 +02:00
|
|
|
BKE_report(
|
2022-10-13 12:24:03 +02:00
|
|
|
reports,
|
|
|
|
|
RPT_ERROR,
|
|
|
|
|
"Cannot apply constructive modifiers on curve. Convert curve to mesh in order to apply");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2010-03-05 16:47:52 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-18 07:38:51 +00:00
|
|
|
BKE_report(reports,
|
|
|
|
|
RPT_INFO,
|
|
|
|
|
"Applied modifier only changed CV points, not tessellated/bevel vertices");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-03-28 12:29:47 +11:00
|
|
|
int verts_num;
|
|
|
|
|
float(*vertexCos)[3] = BKE_curve_nurbs_vert_coords_alloc(&curve_eval->nurb, &verts_num);
|
2023-11-14 10:54:57 +01:00
|
|
|
mti->deform_verts(
|
|
|
|
|
md_eval, &mectx, nullptr, {reinterpret_cast<float3 *>(vertexCos), verts_num});
|
2019-08-22 13:45:31 +10:00
|
|
|
BKE_curve_nurbs_vert_coords_apply(&curve->nurb, vertexCos, false);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-05-21 15:34:09 +00:00
|
|
|
MEM_freeN(vertexCos);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
2020-10-27 13:27:08 +11:00
|
|
|
else if (ob->type == OB_LATTICE) {
|
|
|
|
|
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
|
2022-04-18 12:40:23 -05:00
|
|
|
Lattice *lattice = static_cast<Lattice *>(ob->data);
|
2024-08-01 13:36:43 +02:00
|
|
|
ModifierEvalContext mectx = {depsgraph, object_eval, MOD_APPLY_TO_ORIGINAL};
|
2020-10-27 13:27:08 +11:00
|
|
|
|
2023-11-14 10:03:56 +01:00
|
|
|
if (ELEM(mti->type, ModifierTypeType::Constructive, ModifierTypeType::Nonconstructive)) {
|
2020-10-27 13:27:08 +11:00
|
|
|
BKE_report(reports, RPT_ERROR, "Constructive modifiers cannot be applied");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2020-10-27 13:27:08 +11:00
|
|
|
}
|
|
|
|
|
|
2022-03-28 12:29:47 +11:00
|
|
|
int verts_num;
|
|
|
|
|
float(*vertexCos)[3] = BKE_lattice_vert_coords_alloc(lattice, &verts_num);
|
2023-11-14 10:54:57 +01:00
|
|
|
mti->deform_verts(
|
|
|
|
|
md_eval, &mectx, nullptr, {reinterpret_cast<float3 *>(vertexCos), verts_num});
|
2020-10-27 13:27:08 +11:00
|
|
|
BKE_lattice_vert_coords_apply(lattice, vertexCos);
|
|
|
|
|
|
|
|
|
|
MEM_freeN(vertexCos);
|
|
|
|
|
|
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
}
|
2022-04-26 08:07:54 -05:00
|
|
|
else if (ob->type == OB_CURVES) {
|
|
|
|
|
Curves &curves = *static_cast<Curves *>(ob->data);
|
2023-07-27 12:04:18 +10:00
|
|
|
if (mti->modify_geometry_set == nullptr) {
|
2022-04-26 08:07:54 -05:00
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-03 17:09:18 +02:00
|
|
|
bke::GeometrySet geometry_set = bke::GeometrySet::from_curves(
|
2023-06-15 22:18:28 +02:00
|
|
|
&curves, bke::GeometryOwnershipType::ReadOnly);
|
2022-04-26 08:07:54 -05:00
|
|
|
|
2024-08-01 13:36:43 +02:00
|
|
|
ModifierEvalContext mectx = {depsgraph, ob, MOD_APPLY_TO_ORIGINAL};
|
2023-07-27 12:04:18 +10:00
|
|
|
mti->modify_geometry_set(md_eval, &mectx, &geometry_set);
|
2022-04-26 08:07:54 -05:00
|
|
|
if (!geometry_set.has_curves()) {
|
|
|
|
|
BKE_report(reports, RPT_ERROR, "Evaluated geometry from modifier does not contain curves");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
Curves &curves_eval = *geometry_set.get_curves_for_write();
|
|
|
|
|
|
2023-07-07 09:59:55 -04:00
|
|
|
/* Anonymous attributes shouldn't be available on original geometry. */
|
2023-01-31 18:45:34 +01:00
|
|
|
curves_eval.geometry.wrap().attributes_for_write().remove_anonymous();
|
2022-04-26 08:07:54 -05:00
|
|
|
|
2023-01-31 18:45:34 +01:00
|
|
|
curves.geometry.wrap() = std::move(curves_eval.geometry.wrap());
|
2022-07-21 12:23:10 +02:00
|
|
|
Main *bmain = DEG_get_bmain(depsgraph);
|
|
|
|
|
BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id);
|
2022-04-26 08:07:54 -05:00
|
|
|
}
|
2022-10-12 19:16:22 -05:00
|
|
|
else if (ob->type == OB_POINTCLOUD) {
|
|
|
|
|
PointCloud &points = *static_cast<PointCloud *>(ob->data);
|
2023-07-27 12:04:18 +10:00
|
|
|
if (mti->modify_geometry_set == nullptr) {
|
2022-10-12 19:16:22 -05:00
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-03 17:09:18 +02:00
|
|
|
bke::GeometrySet geometry_set = bke::GeometrySet::from_pointcloud(
|
2023-06-15 22:18:28 +02:00
|
|
|
&points, bke::GeometryOwnershipType::ReadOnly);
|
2022-10-12 19:16:22 -05:00
|
|
|
|
2024-08-01 13:36:43 +02:00
|
|
|
ModifierEvalContext mectx = {depsgraph, ob, MOD_APPLY_TO_ORIGINAL};
|
2023-07-27 12:04:18 +10:00
|
|
|
mti->modify_geometry_set(md_eval, &mectx, &geometry_set);
|
2022-10-12 19:16:22 -05:00
|
|
|
if (!geometry_set.has_pointcloud()) {
|
|
|
|
|
BKE_report(
|
|
|
|
|
reports, RPT_ERROR, "Evaluated geometry from modifier does not contain a point cloud");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
PointCloud *pointcloud_eval =
|
2023-06-15 22:18:28 +02:00
|
|
|
geometry_set.get_component_for_write<bke::PointCloudComponent>().release();
|
2022-10-12 19:16:22 -05:00
|
|
|
|
2023-07-07 09:59:55 -04:00
|
|
|
/* Anonymous attributes shouldn't be available on original geometry. */
|
2022-10-12 19:16:22 -05:00
|
|
|
pointcloud_eval->attributes_for_write().remove_anonymous();
|
|
|
|
|
|
|
|
|
|
Main *bmain = DEG_get_bmain(depsgraph);
|
|
|
|
|
BKE_object_material_from_eval_data(bmain, ob, &pointcloud_eval->id);
|
2023-04-19 15:52:06 -04:00
|
|
|
BKE_pointcloud_nomain_to_pointcloud(pointcloud_eval, &points);
|
2022-10-12 19:16:22 -05:00
|
|
|
}
|
2024-08-13 15:02:25 +02:00
|
|
|
else if (ob->type == OB_GREASE_PENCIL) {
|
|
|
|
|
if (mti->modify_geometry_set == nullptr) {
|
2024-10-15 13:00:47 +02:00
|
|
|
BKE_report(reports, RPT_ERROR, "Cannot apply this modifier to Grease Pencil geometry");
|
2024-08-13 15:02:25 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
GreasePencil &grease_pencil_orig = *static_cast<GreasePencil *>(ob->data);
|
2024-10-04 11:51:30 +02:00
|
|
|
bool success = false;
|
|
|
|
|
if (do_all_keyframes) {
|
2024-11-04 10:38:46 +01:00
|
|
|
/* The function #apply_grease_pencil_for_modifier_all_keyframes will retrieve
|
|
|
|
|
* the evaluated modifier for each keyframe. The original modifier is passed
|
|
|
|
|
* to ensure the evaluated modifier is not used, as it will be invalid when
|
|
|
|
|
* the scene graph is updated for the next keyframe. */
|
|
|
|
|
ModifierData *md = BKE_modifier_get_original(ob, md_eval);
|
2024-10-04 11:51:30 +02:00
|
|
|
success = apply_grease_pencil_for_modifier_all_keyframes(
|
2024-11-04 10:38:46 +01:00
|
|
|
depsgraph, scene, ob, grease_pencil_orig, md);
|
2024-10-04 11:51:30 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
success = apply_grease_pencil_for_modifier(depsgraph, ob, grease_pencil_orig, md_eval);
|
|
|
|
|
}
|
|
|
|
|
if (!success) {
|
|
|
|
|
BKE_report(reports,
|
|
|
|
|
RPT_ERROR,
|
2024-10-15 13:00:47 +02:00
|
|
|
"Evaluated geometry from modifier does not contain Grease Pencil geometry");
|
2024-08-13 15:02:25 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-05-21 15:34:09 +00:00
|
|
|
else {
|
2022-10-12 19:16:22 -05:00
|
|
|
/* TODO: implement for volumes. */
|
2009-05-21 15:34:09 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type");
|
2021-01-08 23:09:31 -06:00
|
|
|
return false;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-09-02 08:06:53 +00:00
|
|
|
/* lattice modifier can be applied to particle system too */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (ob->particlesystem.first) {
|
2020-10-09 15:08:01 -05:00
|
|
|
LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (psys->part->type != PART_HAIR) {
|
2010-09-02 08:06:53 +00:00
|
|
|
continue;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-04-06 12:07:27 +02:00
|
|
|
psys_apply_hair_lattice(depsgraph, scene, ob, psys);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2010-09-02 08:06:53 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-01-08 23:09:31 -06:00
|
|
|
return true;
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
bool modifier_apply(Main *bmain,
|
|
|
|
|
ReportList *reports,
|
|
|
|
|
Depsgraph *depsgraph,
|
|
|
|
|
Scene *scene,
|
|
|
|
|
Object *ob,
|
|
|
|
|
ModifierData *md,
|
|
|
|
|
int mode,
|
2024-10-04 11:51:30 +02:00
|
|
|
bool keep_modifier,
|
|
|
|
|
const bool do_all_keyframes)
|
2009-11-28 04:04:01 +00:00
|
|
|
{
|
2018-02-13 20:35:29 +11:00
|
|
|
if (BKE_object_is_in_editmode(ob)) {
|
2012-10-26 17:32:50 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in edit mode");
|
2020-02-24 15:01:34 +01:00
|
|
|
return false;
|
2012-03-24 06:38:07 +00:00
|
|
|
}
|
2020-07-01 17:38:07 +03:00
|
|
|
if (mode != MODIFIER_APPLY_SHAPE && ID_REAL_USERS(ob->data) > 1) {
|
2009-11-28 04:04:01 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
|
2020-02-24 15:01:34 +01:00
|
|
|
return false;
|
2009-11-28 04:04:01 +00:00
|
|
|
}
|
2022-10-07 22:52:53 +11:00
|
|
|
if ((ob->mode & OB_MODE_SCULPT) && find_multires_modifier_before(scene, md) &&
|
2020-07-03 15:42:22 +02:00
|
|
|
(BKE_modifier_is_same_topology(md) == false))
|
|
|
|
|
{
|
2012-11-13 14:21:55 +00:00
|
|
|
BKE_report(reports,
|
|
|
|
|
RPT_ERROR,
|
|
|
|
|
"Constructive modifier cannot be applied to multi-res data in sculpt mode");
|
2020-02-24 15:01:34 +01:00
|
|
|
return false;
|
2012-11-13 14:21:55 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (md != ob->modifiers.first) {
|
2011-09-19 12:26:20 +00:00
|
|
|
BKE_report(reports, RPT_INFO, "Applied modifier was not first, result may not be as expected");
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-13 18:09:51 +02:00
|
|
|
/* Get evaluated modifier, so object links pointer to evaluated data,
|
2018-09-02 18:28:27 +10:00
|
|
|
* but still use original object it is applied to the original mesh. */
|
2018-07-13 18:09:51 +02:00
|
|
|
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
|
2020-05-25 11:39:52 +02:00
|
|
|
ModifierData *md_eval = (ob_eval) ? BKE_modifiers_findby_name(ob_eval, md->name) : md;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-11-08 14:02:29 +01:00
|
|
|
Depsgraph *apply_depsgraph = depsgraph;
|
|
|
|
|
Depsgraph *local_depsgraph = nullptr;
|
|
|
|
|
|
|
|
|
|
/* If the object is hidden or the modifier is not enabled for the viewport is disabled a special
|
|
|
|
|
* handling is required. This is because the viewport dependency graph optimizes out evaluation
|
|
|
|
|
* of objects which are used by hidden objects and disabled modifiers.
|
|
|
|
|
*
|
|
|
|
|
* The idea is to create a dependency graph which does not perform those optimizations. */
|
|
|
|
|
if ((ob_eval->base_flag & BASE_ENABLED_VIEWPORT) == 0 ||
|
|
|
|
|
(md_eval->mode & eModifierMode_Realtime) == 0)
|
|
|
|
|
{
|
|
|
|
|
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
|
|
|
|
|
|
|
|
|
|
local_depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT);
|
|
|
|
|
DEG_disable_visibility_optimization(local_depsgraph);
|
|
|
|
|
|
2024-04-16 16:39:42 -04:00
|
|
|
DEG_graph_build_from_ids(local_depsgraph, {&ob->id});
|
2022-11-08 14:02:29 +01:00
|
|
|
DEG_evaluate_on_refresh(local_depsgraph);
|
|
|
|
|
|
|
|
|
|
apply_depsgraph = local_depsgraph;
|
|
|
|
|
|
|
|
|
|
/* The evaluated object and modifier are now from the different dependency graph. */
|
|
|
|
|
ob_eval = DEG_get_evaluated_object(local_depsgraph, ob);
|
|
|
|
|
md_eval = BKE_modifiers_findby_name(ob_eval, md->name);
|
|
|
|
|
|
|
|
|
|
/* Force mode on the evaluated modifier, enforcing the modifier evaluation in the apply()
|
|
|
|
|
* functions. */
|
|
|
|
|
md_eval->mode |= eModifierMode_Realtime;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-11-08 14:02:29 +01:00
|
|
|
bool did_apply = false;
|
2009-11-28 04:04:01 +00:00
|
|
|
if (mode == MODIFIER_APPLY_SHAPE) {
|
2022-11-08 14:02:29 +01:00
|
|
|
did_apply = modifier_apply_shape(bmain, reports, apply_depsgraph, scene, ob, md_eval);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-03-24 06:38:07 +00:00
|
|
|
else {
|
2024-10-04 11:51:30 +02:00
|
|
|
did_apply = modifier_apply_obdata(
|
|
|
|
|
reports, apply_depsgraph, scene, ob, md_eval, do_all_keyframes);
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-11-08 14:02:29 +01:00
|
|
|
if (did_apply) {
|
|
|
|
|
if (!keep_modifier) {
|
|
|
|
|
BKE_modifier_remove_from_list(ob, md);
|
|
|
|
|
BKE_modifier_free(md);
|
|
|
|
|
}
|
|
|
|
|
BKE_object_free_derived_caches(ob);
|
2020-07-01 17:38:07 +03:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-11-08 14:02:29 +01:00
|
|
|
if (local_depsgraph != nullptr) {
|
|
|
|
|
DEG_graph_free(local_depsgraph);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-02-24 15:01:34 +01:00
|
|
|
return true;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
bool modifier_copy(
|
2022-10-03 17:37:25 -05:00
|
|
|
ReportList * /*reports*/, Main *bmain, Scene *scene, Object *ob, ModifierData *md)
|
2009-05-21 15:34:09 +00:00
|
|
|
{
|
2020-07-09 11:44:11 +02:00
|
|
|
if (md->type == eModifierType_ParticleSystem) {
|
2021-01-08 23:10:58 -06:00
|
|
|
ModifierData *nmd = object_copy_particle_system(
|
|
|
|
|
bmain, scene, ob, ((ParticleSystemModifierData *)md)->psys);
|
2020-07-09 11:44:11 +02:00
|
|
|
BLI_remlink(&ob->modifiers, nmd);
|
|
|
|
|
BLI_insertlinkafter(&ob->modifiers, md, nmd);
|
2020-12-02 13:35:07 +01:00
|
|
|
BKE_object_modifier_set_active(ob, nmd);
|
2020-07-09 11:44:11 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
|
2021-01-08 23:09:31 -06:00
|
|
|
ModifierData *nmd = BKE_modifier_new(md->type);
|
2020-08-07 11:23:02 +02:00
|
|
|
BKE_modifier_copydata(md, nmd);
|
|
|
|
|
BLI_insertlinkafter(&ob->modifiers, md, nmd);
|
|
|
|
|
BKE_modifier_unique_name(&ob->modifiers, nmd);
|
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(*ob, *nmd);
|
2020-12-02 13:35:07 +01:00
|
|
|
BKE_object_modifier_set_active(ob, nmd);
|
2009-05-21 15:34:09 +00:00
|
|
|
|
2020-08-12 14:21:40 +02:00
|
|
|
nmd->flag |= eModifierFlag_OverrideLibrary_Local;
|
|
|
|
|
|
2021-01-08 23:09:31 -06:00
|
|
|
return true;
|
2009-05-21 15:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
Vector<PointerRNA> modifier_get_edit_objects(const bContext &C, const wmOperator &op)
|
|
|
|
|
{
|
|
|
|
|
Vector<PointerRNA> objects;
|
|
|
|
|
if (RNA_boolean_get(op.ptr, "use_selected_objects")) {
|
|
|
|
|
CTX_data_selected_editable_objects(&C, &objects);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (Object *object = context_active_object(&C)) {
|
|
|
|
|
objects.append(RNA_id_pointer_create(&object->id));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return objects;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void modifier_register_use_selected_objects_prop(wmOperatorType *ot)
|
|
|
|
|
{
|
2024-04-19 14:00:49 +02:00
|
|
|
PropertyRNA *prop = RNA_def_boolean(
|
|
|
|
|
ot->srna,
|
|
|
|
|
"use_selected_objects",
|
|
|
|
|
false,
|
|
|
|
|
"Selected Objects",
|
|
|
|
|
"Affect all selected objects instead of just the active object");
|
2024-05-01 14:15:53 +02:00
|
|
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
|
2024-04-18 17:40:50 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Add Modifier Operator
|
|
|
|
|
* \{ */
|
2009-04-27 18:05:58 +00:00
|
|
|
|
|
|
|
|
static int modifier_add_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);
|
|
|
|
|
int type = RNA_enum_get(op->ptr, "type");
|
2009-04-27 18:05:58 +00:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
bool changed = false;
|
|
|
|
|
for (const PointerRNA &ptr : modifier_get_edit_objects(*C, *op)) {
|
|
|
|
|
Object *ob = static_cast<Object *>(ptr.data);
|
|
|
|
|
if (!modifier_add(op->reports, bmain, scene, ob, nullptr, type)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
changed = true;
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
}
|
|
|
|
|
if (!changed) {
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-04-27 18:05:58 +00:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
static int modifier_add_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
2024-05-01 14:15:53 +02:00
|
|
|
if (event->modifier & KM_ALT || CTX_wm_view3d(C)) {
|
2024-04-18 17:40:50 +02:00
|
|
|
RNA_boolean_set(op->ptr, "use_selected_objects", true);
|
|
|
|
|
}
|
|
|
|
|
if (!RNA_struct_property_is_set(op->ptr, "type")) {
|
|
|
|
|
return WM_menu_invoke(C, op, event);
|
|
|
|
|
}
|
|
|
|
|
return modifier_add_exec(C, op);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 15:07:26 +11:00
|
|
|
static const EnumPropertyItem *modifier_add_itemf(bContext *C,
|
2022-10-03 17:37:25 -05:00
|
|
|
PointerRNA * /*ptr*/,
|
|
|
|
|
PropertyRNA * /*prop*/,
|
2018-07-31 10:22:19 +02:00
|
|
|
bool *r_free)
|
2018-06-04 09:31:30 +02:00
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!ob) {
|
2015-11-23 13:49:52 +11:00
|
|
|
return rna_enum_object_modifier_type_items;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-08-21 02:51:56 +00:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
EnumPropertyItem *items = nullptr;
|
2020-10-09 15:08:01 -05:00
|
|
|
int totitem = 0;
|
|
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
const EnumPropertyItem *group_item = nullptr;
|
2020-10-09 15:08:01 -05:00
|
|
|
for (int a = 0; rna_enum_object_modifier_type_items[a].identifier; a++) {
|
|
|
|
|
const EnumPropertyItem *md_item = &rna_enum_object_modifier_type_items[a];
|
2009-08-21 02:51:56 +00:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (md_item->identifier[0]) {
|
2022-04-18 12:40:23 -05:00
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_item->value);
|
2009-08-21 02:51:56 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (mti->flags & eModifierTypeFlag_NoUserAdd) {
|
2009-08-21 02:51:56 +00:00
|
|
|
continue;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-08-21 02:51:56 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!BKE_object_support_modifier_type_check(ob, md_item->value)) {
|
2009-08-21 02:51:56 +00:00
|
|
|
continue;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-08-21 02:51:56 +00:00
|
|
|
}
|
2011-12-21 13:31:28 +00:00
|
|
|
else {
|
2012-04-28 15:42:27 +00:00
|
|
|
group_item = md_item;
|
2011-12-21 13:31:28 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (group_item) {
|
2020-10-09 15:08:01 -05:00
|
|
|
RNA_enum_item_add(&items, &totitem, group_item);
|
2022-04-18 12:40:23 -05:00
|
|
|
group_item = nullptr;
|
2011-12-21 13:31:28 +00:00
|
|
|
}
|
2009-08-21 02:51:56 +00:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
RNA_enum_item_add(&items, &totitem, md_item);
|
2009-08-21 02:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
RNA_enum_item_end(&items, &totitem);
|
2014-01-04 18:08:43 +11:00
|
|
|
*r_free = true;
|
2009-08-21 02:51:56 +00:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
return items;
|
2009-08-21 02:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
2009-04-27 18:05:58 +00:00
|
|
|
void OBJECT_OT_modifier_add(wmOperatorType *ot)
|
|
|
|
|
{
|
2009-08-21 02:51:56 +00:00
|
|
|
PropertyRNA *prop;
|
|
|
|
|
|
2009-04-27 18:05:58 +00:00
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Add Modifier";
|
2017-04-13 18:07:10 -04:00
|
|
|
ot->description = "Add a procedural operation/effect to the active object";
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->idname = "OBJECT_OT_modifier_add";
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-04-27 18:05:58 +00:00
|
|
|
/* api callbacks */
|
2024-04-18 17:40:50 +02:00
|
|
|
ot->invoke = modifier_add_invoke;
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = modifier_add_exec;
|
|
|
|
|
ot->poll = ED_operator_object_active_editable;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-04-27 18:05:58 +00:00
|
|
|
/* flags */
|
2012-04-28 15:42:27 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-08-21 02:51:56 +00:00
|
|
|
/* properties */
|
2015-11-23 13:49:52 +11:00
|
|
|
prop = RNA_def_enum(
|
|
|
|
|
ot->srna, "type", rna_enum_object_modifier_type_items, eModifierType_Subsurf, "Type", "");
|
2009-08-21 02:51:56 +00:00
|
|
|
RNA_def_enum_funcs(prop, modifier_add_itemf);
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->prop = prop;
|
2024-04-18 17:40:50 +02:00
|
|
|
modifier_register_use_selected_objects_prop(ot);
|
2009-04-27 18:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
2020-12-02 13:35:07 +01:00
|
|
|
/** \name Generic Poll Function and Properties
|
2020-03-26 10:24:44 +11:00
|
|
|
*
|
|
|
|
|
* Using modifier names and data context.
|
|
|
|
|
* \{ */
|
2010-04-22 01:55:10 +00:00
|
|
|
|
2019-07-28 16:07:15 +02:00
|
|
|
bool edit_modifier_poll_generic(bContext *C,
|
|
|
|
|
StructRNA *rna_type,
|
|
|
|
|
int obtype_flag,
|
2020-08-12 14:21:40 +02:00
|
|
|
const bool is_editmode_allowed,
|
|
|
|
|
const bool is_liboverride_allowed)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2022-03-28 17:34:36 +02:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2012-04-28 15:42:27 +00:00
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : context_active_object(C);
|
2022-04-18 12:40:23 -05:00
|
|
|
ModifierData *mod = static_cast<ModifierData *>(ptr.data); /* May be nullptr. */
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if (mod == nullptr && ob != nullptr) {
|
2021-09-29 12:52:48 +02:00
|
|
|
mod = BKE_object_active_modifier(ob);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-28 17:34:36 +02:00
|
|
|
if (!ob || !BKE_id_is_editable(bmain, &ob->id)) {
|
2020-02-24 15:01:34 +01:00
|
|
|
return false;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) {
|
2020-02-24 15:01:34 +01:00
|
|
|
return false;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2022-03-28 17:34:36 +02:00
|
|
|
if (ptr.owner_id && !BKE_id_is_editable(bmain, ptr.owner_id)) {
|
2020-02-24 15:01:34 +01:00
|
|
|
return false;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2018-05-02 18:45:22 +02:00
|
|
|
|
2020-12-08 09:40:42 +01:00
|
|
|
if (!is_liboverride_allowed && BKE_modifier_is_nonlocal_in_liboverride(ob, mod)) {
|
|
|
|
|
CTX_wm_operator_poll_msg_set(
|
|
|
|
|
C, "Cannot edit modifiers coming from linked data in a library override");
|
2020-12-07 16:52:45 +01:00
|
|
|
return false;
|
2018-05-02 18:45:22 +02:00
|
|
|
}
|
2018-06-04 09:39:04 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if (!is_editmode_allowed && CTX_data_edit_object(C) != nullptr) {
|
2019-07-28 16:07:15 +02:00
|
|
|
CTX_wm_operator_poll_msg_set(C, "This modifier operation is not allowed from Edit mode");
|
2020-02-24 15:01:34 +01:00
|
|
|
return false;
|
2019-07-28 16:07:15 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-24 15:01:34 +01:00
|
|
|
return true;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2020-08-12 14:21:40 +02:00
|
|
|
static bool edit_modifier_poll(bContext *C)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2020-08-12 14:21:40 +02:00
|
|
|
return edit_modifier_poll_generic(C, &RNA_Modifier, 0, true, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Used by operators performing actions allowed also on modifiers from the overridden linked object
|
|
|
|
|
* (not only from added 'local' ones). */
|
|
|
|
|
static bool edit_modifier_liboverride_allowed_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
return edit_modifier_poll_generic(C, &RNA_Modifier, 0, true, true);
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2015-01-09 21:19:12 +01:00
|
|
|
void edit_modifier_properties(wmOperatorType *ot)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2018-08-16 08:41:02 +10:00
|
|
|
PropertyRNA *prop = RNA_def_string(
|
2022-04-18 12:40:23 -05:00
|
|
|
ot->srna, "modifier", nullptr, MAX_NAME, "Modifier", "Name of the modifier to edit");
|
2018-08-16 08:41:02 +10:00
|
|
|
RNA_def_property_flag(prop, PROP_HIDDEN);
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2020-06-29 15:00:25 -04:00
|
|
|
static void edit_modifier_report_property(wmOperatorType *ot)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2020-06-29 15:00:25 -04:00
|
|
|
PropertyRNA *prop = RNA_def_boolean(
|
|
|
|
|
ot->srna, "report", false, "Report", "Create a notification after the operation");
|
|
|
|
|
RNA_def_property_flag(prop, PROP_HIDDEN);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-02 13:35:07 +01:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Generic Invoke Functions
|
|
|
|
|
*
|
|
|
|
|
* Using modifier names and data context.
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
bool edit_modifier_invoke_properties(bContext *C, wmOperator *op)
|
2020-06-29 15:00:25 -04:00
|
|
|
{
|
2012-06-29 23:09:19 +00:00
|
|
|
if (RNA_struct_property_is_set(op->ptr, "modifier")) {
|
2014-04-01 11:34:00 +11:00
|
|
|
return true;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
2020-06-29 21:04:45 -04:00
|
|
|
|
|
|
|
|
PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
|
2022-04-18 12:40:23 -05:00
|
|
|
if (ctx_ptr.data != nullptr) {
|
|
|
|
|
ModifierData *md = static_cast<ModifierData *>(ctx_ptr.data);
|
2020-06-29 15:00:25 -04:00
|
|
|
RNA_string_set(op->ptr, "modifier", md->name);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2020-06-29 21:04:45 -04:00
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
return false;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
/**
|
2021-04-26 22:16:12 -05:00
|
|
|
* If the "modifier" property is not set, fill the modifier property with the name of the modifier
|
|
|
|
|
* with a UI panel below the mouse cursor, unless a specific modifier is set with a context
|
|
|
|
|
* pointer. Used in order to apply modifier operators on hover over their panels.
|
2021-03-30 09:56:04 -05:00
|
|
|
*/
|
2021-04-26 22:19:39 -05:00
|
|
|
static bool edit_modifier_invoke_properties_with_hover(bContext *C,
|
|
|
|
|
wmOperator *op,
|
|
|
|
|
const wmEvent *event,
|
|
|
|
|
int *r_retval)
|
2021-03-30 09:56:04 -05:00
|
|
|
{
|
2024-04-18 17:40:50 +02:00
|
|
|
if (RNA_struct_find_property(op->ptr, "use_selected_objects")) {
|
|
|
|
|
if (event->modifier & KM_ALT) {
|
|
|
|
|
RNA_boolean_set(op->ptr, "use_selected_objects", true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
if (RNA_struct_property_is_set(op->ptr, "modifier")) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-26 22:16:12 -05:00
|
|
|
/* Note that the context pointer is *not* the active modifier, it is set in UI layouts. */
|
|
|
|
|
PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
|
2022-04-18 12:40:23 -05:00
|
|
|
if (ctx_ptr.data != nullptr) {
|
|
|
|
|
ModifierData *md = static_cast<ModifierData *>(ctx_ptr.data);
|
2021-04-26 22:16:12 -05:00
|
|
|
RNA_string_set(op->ptr, "modifier", md->name);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
|
2022-04-18 12:40:23 -05:00
|
|
|
if (panel_ptr == nullptr || RNA_pointer_is_null(panel_ptr)) {
|
2021-03-30 09:56:04 -05:00
|
|
|
*r_retval = OPERATOR_CANCELLED;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!RNA_struct_is_a(panel_ptr->type, &RNA_Modifier)) {
|
2021-03-30 10:03:11 -05:00
|
|
|
/* Work around multiple operators using the same shortcut. The operators for the other
|
|
|
|
|
* stacks in the property editor use the same key, and will not run after these return
|
|
|
|
|
* OPERATOR_CANCELLED. */
|
2021-03-30 09:56:04 -05:00
|
|
|
*r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
const ModifierData *md = static_cast<const ModifierData *>(panel_ptr->data);
|
2021-03-30 09:56:04 -05:00
|
|
|
RNA_string_set(op->ptr, "modifier", md->name);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-09 21:19:12 +01:00
|
|
|
ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2012-01-11 08:51:06 +00:00
|
|
|
char modifier_name[MAX_NAME];
|
2010-04-22 01:55:10 +00:00
|
|
|
RNA_string_get(op->ptr, "modifier", modifier_name);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
ModifierData *md = BKE_modifiers_findby_name(ob, modifier_name);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (md && type != 0 && md->type != type) {
|
2022-04-18 12:40:23 -05:00
|
|
|
md = nullptr;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2010-04-22 01:55:10 +00:00
|
|
|
|
|
|
|
|
return md;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Remove Modifier Operator
|
|
|
|
|
* \{ */
|
2009-07-02 19:41:31 +00:00
|
|
|
|
|
|
|
|
static int modifier_remove_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2012-04-28 15:42:27 +00:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2020-07-09 11:44:11 +02:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2017-11-23 13:51:49 -02:00
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2020-06-29 15:00:25 -04:00
|
|
|
char name[MAX_NAME];
|
2024-04-18 17:40:50 +02:00
|
|
|
RNA_string_get(op->ptr, "modifier", name);
|
2020-06-29 15:00:25 -04:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
bool changed = false;
|
|
|
|
|
for (const PointerRNA &ptr : modifier_get_edit_objects(*C, *op)) {
|
|
|
|
|
Object *ob = static_cast<Object *>(ptr.data);
|
|
|
|
|
ModifierData *md = BKE_modifiers_findby_name(ob, name);
|
|
|
|
|
if (md == nullptr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
int mode_orig = ob->mode;
|
|
|
|
|
if (!modifier_remove(op->reports, bmain, scene, ob, md)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
changed = true;
|
2010-10-19 10:17:19 +00:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
|
|
|
|
/* if cloth/softbody was removed, particle mode could be cleared */
|
|
|
|
|
if (mode_orig & OB_MODE_PARTICLE_EDIT) {
|
|
|
|
|
if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
|
|
|
|
|
BKE_view_layer_synced_ensure(scene, view_layer);
|
|
|
|
|
if (ob == BKE_view_layer_active_object_get(view_layer)) {
|
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, nullptr);
|
|
|
|
|
}
|
2018-02-06 17:53:19 +11:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-29 15:00:25 -04:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
if (!changed) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 15:00:25 -04:00
|
|
|
if (RNA_boolean_get(op->ptr, "report")) {
|
|
|
|
|
BKE_reportf(op->reports, RPT_INFO, "Removed modifier: %s", name);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
static int modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2021-03-30 09:56:04 -05:00
|
|
|
int retval;
|
2021-04-26 22:16:12 -05:00
|
|
|
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
|
2010-04-22 01:55:10 +00:00
|
|
|
return modifier_remove_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2021-03-30 09:56:04 -05:00
|
|
|
return retval;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
void OBJECT_OT_modifier_remove(wmOperatorType *ot)
|
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Remove Modifier";
|
|
|
|
|
ot->description = "Remove a modifier from the active object";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifier_remove";
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->invoke = modifier_remove_invoke;
|
|
|
|
|
ot->exec = modifier_remove_exec;
|
|
|
|
|
ot->poll = edit_modifier_poll;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2010-04-22 01:55:10 +00:00
|
|
|
edit_modifier_properties(ot);
|
2020-06-29 15:00:25 -04:00
|
|
|
edit_modifier_report_property(ot);
|
2024-04-18 17:40:50 +02:00
|
|
|
modifier_register_use_selected_objects_prop(ot);
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
2024-05-01 14:15:53 +02:00
|
|
|
static int modifiers_clear_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
{
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
|
|
|
|
|
CTX_DATA_BEGIN (C, Object *, object, selected_editable_objects) {
|
|
|
|
|
modifiers_clear(bmain, scene, object);
|
|
|
|
|
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object);
|
|
|
|
|
}
|
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-02 10:12:46 -04:00
|
|
|
static bool modifiers_clear_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
if (!ED_operator_object_active_local_editable(C)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const Object *object = context_active_object(C);
|
|
|
|
|
if (!BKE_object_supports_modifiers(object)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 14:15:53 +02:00
|
|
|
void OBJECT_OT_modifiers_clear(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Clear Object Modifiers";
|
|
|
|
|
ot->description = "Clear all modifiers from the selected objects";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifiers_clear";
|
|
|
|
|
|
|
|
|
|
ot->exec = modifiers_clear_exec;
|
2024-05-02 10:12:46 -04:00
|
|
|
ot->poll = modifiers_clear_poll;
|
2024-05-01 14:15:53 +02:00
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Move Up Modifier Operator
|
|
|
|
|
* \{ */
|
2009-07-02 19:41:31 +00:00
|
|
|
|
|
|
|
|
static int modifier_move_up_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2010-10-16 14:32:17 +00:00
|
|
|
ModifierData *md = edit_modifier_property_get(op, ob, 0);
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
if (!md || !modifier_move_up(op->reports, RPT_WARNING, ob, md)) {
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
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);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
static int modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2021-03-30 09:56:04 -05:00
|
|
|
int retval;
|
2021-04-26 22:16:12 -05:00
|
|
|
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
|
2010-04-22 01:55:10 +00:00
|
|
|
return modifier_move_up_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2021-03-30 09:56:04 -05:00
|
|
|
return retval;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
void OBJECT_OT_modifier_move_up(wmOperatorType *ot)
|
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Move Up Modifier";
|
|
|
|
|
ot->description = "Move modifier up in the stack";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifier_move_up";
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->invoke = modifier_move_up_invoke;
|
|
|
|
|
ot->exec = modifier_move_up_exec;
|
|
|
|
|
ot->poll = edit_modifier_poll;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2010-04-22 01:55:10 +00:00
|
|
|
edit_modifier_properties(ot);
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Move Down Modifier Operator
|
|
|
|
|
* \{ */
|
2009-07-02 19:41:31 +00:00
|
|
|
|
|
|
|
|
static int modifier_move_down_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2010-10-16 14:32:17 +00:00
|
|
|
ModifierData *md = edit_modifier_property_get(op, ob, 0);
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
if (!md || !modifier_move_down(op->reports, RPT_WARNING, ob, md)) {
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
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);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
static int modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2021-03-30 09:56:04 -05:00
|
|
|
int retval;
|
2021-04-26 22:16:12 -05:00
|
|
|
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
|
2010-04-22 01:55:10 +00:00
|
|
|
return modifier_move_down_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2021-03-30 09:56:04 -05:00
|
|
|
return retval;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
void OBJECT_OT_modifier_move_down(wmOperatorType *ot)
|
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Move Down Modifier";
|
|
|
|
|
ot->description = "Move modifier down in the stack";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifier_move_down";
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->invoke = modifier_move_down_invoke;
|
|
|
|
|
ot->exec = modifier_move_down_exec;
|
|
|
|
|
ot->poll = edit_modifier_poll;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2010-04-22 01:55:10 +00:00
|
|
|
edit_modifier_properties(ot);
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
2020-06-05 10:41:03 -04:00
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Move to Index Modifier Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static int modifier_move_to_index_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-04-18 17:40:50 +02:00
|
|
|
char name[MAX_NAME];
|
|
|
|
|
RNA_string_get(op->ptr, "modifier", name);
|
|
|
|
|
|
|
|
|
|
const int index = RNA_int_get(op->ptr, "index");
|
|
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
|
for (const PointerRNA &ptr : modifier_get_edit_objects(*C, *op)) {
|
|
|
|
|
Object *ob = static_cast<Object *>(ptr.data);
|
|
|
|
|
ModifierData *md = BKE_modifiers_findby_name(ob, name);
|
|
|
|
|
if (!md) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-06-05 10:41:03 -04:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
if (!modifier_move_to_index(op->reports, RPT_WARNING, ob, md, index, true)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!changed) {
|
2020-06-05 10:41:03 -04:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
static int modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2020-06-05 10:41:03 -04:00
|
|
|
{
|
2021-03-30 09:56:04 -05:00
|
|
|
int retval;
|
2021-04-26 22:16:12 -05:00
|
|
|
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
|
2020-06-05 10:41:03 -04:00
|
|
|
return modifier_move_to_index_exec(C, op);
|
|
|
|
|
}
|
2021-03-30 09:56:04 -05:00
|
|
|
return retval;
|
2020-06-05 10:41:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Move Active Modifier to Index";
|
2020-06-10 11:23:03 -04:00
|
|
|
ot->description =
|
|
|
|
|
"Change the modifier's index in the stack so it evaluates after the set number of others";
|
2020-06-05 10:41:03 -04:00
|
|
|
ot->idname = "OBJECT_OT_modifier_move_to_index";
|
|
|
|
|
|
|
|
|
|
ot->invoke = modifier_move_to_index_invoke;
|
|
|
|
|
ot->exec = modifier_move_to_index_exec;
|
2020-08-12 14:21:40 +02:00
|
|
|
ot->poll = edit_modifier_poll;
|
2020-06-05 10:41:03 -04:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
RNA_def_int(
|
|
|
|
|
ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the modifier to", 0, INT_MAX);
|
2024-04-18 17:40:50 +02:00
|
|
|
modifier_register_use_selected_objects_prop(ot);
|
2020-06-05 10:41:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Apply Modifier Operator
|
|
|
|
|
* \{ */
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2022-03-30 11:06:10 +02:00
|
|
|
static bool modifier_apply_poll(bContext *C)
|
2020-02-24 15:01:34 +01:00
|
|
|
{
|
2020-08-12 14:21:40 +02:00
|
|
|
if (!edit_modifier_poll_generic(C, &RNA_Modifier, 0, false, false)) {
|
2020-02-24 15:01:34 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = (ptr.owner_id != nullptr) ? (Object *)ptr.owner_id : context_active_object(C);
|
2022-04-18 12:40:23 -05:00
|
|
|
ModifierData *md = static_cast<ModifierData *>(ptr.data); /* May be nullptr. */
|
2020-02-24 15:01:34 +01:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if (ID_IS_OVERRIDE_LIBRARY(ob) || ((ob->data != nullptr) && ID_IS_OVERRIDE_LIBRARY(ob->data))) {
|
2020-06-30 17:51:41 +02:00
|
|
|
CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied on override data");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-04-18 12:40:23 -05:00
|
|
|
if (md != nullptr) {
|
2022-10-07 22:52:53 +11:00
|
|
|
if ((ob->mode & OB_MODE_SCULPT) && find_multires_modifier_before(scene, md) &&
|
2020-05-08 10:14:02 +02:00
|
|
|
(BKE_modifier_is_same_topology(md) == false))
|
|
|
|
|
{
|
2020-02-24 15:01:34 +01:00
|
|
|
CTX_wm_operator_poll_msg_set(
|
|
|
|
|
C, "Constructive modifier cannot be applied to multi-res data in sculpt mode");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-01 17:38:07 +03:00
|
|
|
static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, bool keep_modifier)
|
2009-07-02 19:41:31 +00:00
|
|
|
{
|
2018-06-12 12:53:27 +02:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2012-04-28 15:42:27 +00:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2024-04-18 17:40:50 +02:00
|
|
|
Vector<PointerRNA> objects = modifier_get_edit_objects(*C, *op);
|
|
|
|
|
|
|
|
|
|
char name[MAX_NAME];
|
|
|
|
|
RNA_string_get(op->ptr, "modifier", name);
|
|
|
|
|
|
2021-02-16 23:39:58 -06:00
|
|
|
const bool do_report = RNA_boolean_get(op->ptr, "report");
|
2024-04-18 17:40:50 +02:00
|
|
|
const int reports_len = do_report ? BLI_listbase_count(&op->reports->list) : 0;
|
2023-04-30 13:31:19 +10:00
|
|
|
|
|
|
|
|
const bool do_single_user = (apply_as == MODIFIER_APPLY_DATA) ?
|
|
|
|
|
RNA_boolean_get(op->ptr, "single_user") :
|
|
|
|
|
false;
|
|
|
|
|
const bool do_merge_customdata = (apply_as == MODIFIER_APPLY_DATA) ?
|
|
|
|
|
RNA_boolean_get(op->ptr, "merge_customdata") :
|
|
|
|
|
false;
|
2024-10-04 11:51:30 +02:00
|
|
|
const bool do_all_keyframes = (apply_as == MODIFIER_APPLY_DATA) ?
|
|
|
|
|
RNA_boolean_get(op->ptr, "all_keyframes") :
|
|
|
|
|
false;
|
2012-11-13 14:21:55 +00:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
bool changed = false;
|
|
|
|
|
for (const PointerRNA &ptr : objects) {
|
|
|
|
|
Object *ob = static_cast<Object *>(ptr.data);
|
|
|
|
|
ModifierData *md = BKE_modifiers_findby_name(ob, name);
|
|
|
|
|
if (md == nullptr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-06-29 21:04:45 -04:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
2022-07-21 12:52:24 +10:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
if (do_single_user && ID_REAL_USERS(ob->data) > 1) {
|
|
|
|
|
single_obdata_user_make(bmain, scene, ob);
|
|
|
|
|
BKE_main_id_newptr_and_tag_clear(bmain);
|
|
|
|
|
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
|
}
|
2022-03-30 11:06:10 +02:00
|
|
|
|
2024-10-04 11:51:30 +02:00
|
|
|
if (!modifier_apply(bmain,
|
|
|
|
|
op->reports,
|
|
|
|
|
depsgraph,
|
|
|
|
|
scene,
|
|
|
|
|
ob,
|
|
|
|
|
md,
|
|
|
|
|
apply_as,
|
|
|
|
|
keep_modifier,
|
|
|
|
|
do_all_keyframes))
|
|
|
|
|
{
|
2024-04-18 17:40:50 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
changed = true;
|
2020-06-29 15:00:25 -04:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
if (ob->type == OB_MESH && do_merge_customdata &&
|
|
|
|
|
ELEM(mti->type, ModifierTypeType::Constructive, ModifierTypeType::Nonconstructive))
|
|
|
|
|
{
|
|
|
|
|
BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data);
|
|
|
|
|
}
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
2022-05-05 20:36:15 +10:00
|
|
|
}
|
|
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
if (!changed) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2021-02-16 23:39:58 -06:00
|
|
|
if (do_report) {
|
|
|
|
|
/* Only add this report if the operator didn't cause another one. The purpose here is
|
|
|
|
|
* to alert that something happened, and the previous report will do that anyway. */
|
|
|
|
|
if (BLI_listbase_count(&op->reports->list) == reports_len) {
|
|
|
|
|
BKE_reportf(op->reports, RPT_INFO, "Applied modifier: %s", name);
|
|
|
|
|
}
|
2020-06-29 15:00:25 -04:00
|
|
|
}
|
|
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-01 17:38:07 +03:00
|
|
|
static int modifier_apply_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_DATA, false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2021-03-30 09:56:04 -05:00
|
|
|
int retval;
|
2021-04-26 22:16:12 -05:00
|
|
|
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
|
2022-03-30 11:06:10 +02:00
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = (ptr.owner_id != nullptr) ? (Object *)ptr.owner_id : context_active_object(C);
|
2022-03-30 11:06:10 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if ((ob->data != nullptr) && ID_REAL_USERS(ob->data) > 1) {
|
2022-03-30 11:06:10 +02:00
|
|
|
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "single_user");
|
|
|
|
|
if (!RNA_property_is_set(op->ptr, prop)) {
|
|
|
|
|
RNA_property_boolean_set(op->ptr, prop, true);
|
|
|
|
|
}
|
|
|
|
|
if (RNA_property_boolean_get(op->ptr, prop)) {
|
2024-02-24 01:38:34 +01:00
|
|
|
return WM_operator_confirm_ex(
|
|
|
|
|
C,
|
|
|
|
|
op,
|
|
|
|
|
IFACE_("Apply Modifier"),
|
|
|
|
|
IFACE_("Make data single-user, apply modifier, and remove it from the list."),
|
|
|
|
|
IFACE_("Apply"),
|
|
|
|
|
ALERT_ICON_WARNING,
|
|
|
|
|
false);
|
2022-03-30 11:06:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-04-22 01:55:10 +00:00
|
|
|
return modifier_apply_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2021-03-30 09:56:04 -05:00
|
|
|
return retval;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
void OBJECT_OT_modifier_apply(wmOperatorType *ot)
|
|
|
|
|
{
|
2024-10-04 11:51:30 +02:00
|
|
|
PropertyRNA *prop;
|
|
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Apply Modifier";
|
|
|
|
|
ot->description = "Apply modifier and remove from the stack";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifier_apply";
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->invoke = modifier_apply_invoke;
|
|
|
|
|
ot->exec = modifier_apply_exec;
|
2020-02-24 15:01:34 +01:00
|
|
|
ot->poll = modifier_apply_poll;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2020-07-01 17:38:07 +03:00
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
edit_modifier_report_property(ot);
|
2022-03-30 11:06:10 +02:00
|
|
|
|
2022-05-31 10:05:41 +02:00
|
|
|
RNA_def_boolean(ot->srna,
|
|
|
|
|
"merge_customdata",
|
|
|
|
|
true,
|
2023-01-10 14:49:51 -05:00
|
|
|
"Merge UVs",
|
2022-05-31 10:05:41 +02:00
|
|
|
"For mesh objects, merge UV coordinates that share a vertex to account for "
|
|
|
|
|
"imprecision in some modifiers");
|
2024-10-04 11:51:30 +02:00
|
|
|
prop = RNA_def_boolean(ot->srna,
|
|
|
|
|
"single_user",
|
|
|
|
|
false,
|
|
|
|
|
"Make Data Single User",
|
|
|
|
|
"Make the object's data single user if needed");
|
|
|
|
|
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
|
|
|
|
|
prop = RNA_def_boolean(ot->srna,
|
|
|
|
|
"all_keyframes",
|
|
|
|
|
false,
|
|
|
|
|
"Apply to all keyframes",
|
|
|
|
|
"For Grease Pencil objects, apply the modifier to all the keyframes");
|
2022-04-18 12:40:23 -05:00
|
|
|
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
|
2024-04-18 17:40:50 +02:00
|
|
|
modifier_register_use_selected_objects_prop(ot);
|
2020-07-01 17:38:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
2022-09-16 18:13:19 +10:00
|
|
|
/** \name Apply Modifier As Shape-Key Operator
|
2020-07-01 17:38:07 +03:00
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static bool modifier_apply_as_shapekey_poll(bContext *C)
|
|
|
|
|
{
|
2022-03-30 11:06:10 +02:00
|
|
|
return modifier_apply_poll(C);
|
2020-07-01 17:38:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int modifier_apply_as_shapekey_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
bool keep = RNA_boolean_get(op->ptr, "keep_modifier");
|
|
|
|
|
|
|
|
|
|
return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_SHAPE, keep);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
static int modifier_apply_as_shapekey_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2020-07-01 17:38:07 +03:00
|
|
|
{
|
2021-03-30 09:56:04 -05:00
|
|
|
int retval;
|
2021-04-26 22:16:12 -05:00
|
|
|
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
|
2020-07-01 17:38:07 +03:00
|
|
|
return modifier_apply_as_shapekey_exec(C, op);
|
|
|
|
|
}
|
2021-03-30 09:56:04 -05:00
|
|
|
return retval;
|
2020-07-01 17:38:07 +03:00
|
|
|
}
|
|
|
|
|
|
2023-08-11 19:11:27 +02:00
|
|
|
static std::string modifier_apply_as_shapekey_get_description(bContext * /*C*/,
|
2023-10-05 13:55:35 +11:00
|
|
|
wmOperatorType * /*ot*/,
|
2024-05-24 14:08:33 +10:00
|
|
|
PointerRNA *ptr)
|
2020-07-01 17:38:07 +03:00
|
|
|
{
|
2024-05-24 14:08:33 +10:00
|
|
|
bool keep = RNA_boolean_get(ptr, "keep_modifier");
|
2020-07-01 17:38:07 +03:00
|
|
|
if (keep) {
|
2023-08-11 19:11:27 +02:00
|
|
|
return TIP_("Apply modifier as a new shapekey and keep it in the stack");
|
2020-07-01 17:38:07 +03:00
|
|
|
}
|
|
|
|
|
|
2023-08-11 19:11:27 +02:00
|
|
|
return "";
|
2020-07-01 17:38:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot)
|
|
|
|
|
{
|
2020-10-24 11:42:17 -07:00
|
|
|
ot->name = "Apply Modifier as Shape Key";
|
|
|
|
|
ot->description = "Apply modifier as a new shape key and remove from the stack";
|
2020-07-01 17:38:07 +03:00
|
|
|
ot->idname = "OBJECT_OT_modifier_apply_as_shapekey";
|
|
|
|
|
|
|
|
|
|
ot->invoke = modifier_apply_as_shapekey_invoke;
|
|
|
|
|
ot->exec = modifier_apply_as_shapekey_exec;
|
|
|
|
|
ot->poll = modifier_apply_as_shapekey_poll;
|
|
|
|
|
ot->get_description = modifier_apply_as_shapekey_get_description;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
|
|
|
|
|
RNA_def_boolean(
|
|
|
|
|
ot->srna, "keep_modifier", false, "Keep Modifier", "Do not remove the modifier from stack");
|
2010-04-22 01:55:10 +00:00
|
|
|
edit_modifier_properties(ot);
|
2020-06-29 15:00:25 -04:00
|
|
|
edit_modifier_report_property(ot);
|
2024-10-31 18:45:46 +11:00
|
|
|
modifier_register_use_selected_objects_prop(ot);
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
2022-05-31 17:40:02 +10:00
|
|
|
/** \name Convert Particle System Modifier to Mesh Operator
|
2020-03-26 10:24:44 +11:00
|
|
|
* \{ */
|
2009-07-02 19:41:31 +00:00
|
|
|
|
|
|
|
|
static int modifier_convert_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2012-04-28 15:42:27 +00:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2022-09-14 21:30:20 +02:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2017-11-23 13:51:49 -02:00
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2010-10-16 14:32:17 +00:00
|
|
|
ModifierData *md = edit_modifier_property_get(op, ob, 0);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
if (!md || !convert_psys_to_mesh(op->reports, bmain, depsgraph, scene, view_layer, ob, md)) {
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
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);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int modifier_convert_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2020-12-02 13:35:07 +01:00
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
2010-04-22 01:55:10 +00:00
|
|
|
return modifier_convert_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:42:22 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
void OBJECT_OT_modifier_convert(wmOperatorType *ot)
|
|
|
|
|
{
|
2022-05-31 17:40:02 +10:00
|
|
|
ot->name = "Convert Particles to Mesh";
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->description = "Convert particles to a mesh object";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifier_convert";
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->invoke = modifier_convert_invoke;
|
|
|
|
|
ot->exec = modifier_convert_exec;
|
|
|
|
|
ot->poll = edit_modifier_poll;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2010-04-22 01:55:10 +00:00
|
|
|
edit_modifier_properties(ot);
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Copy Modifier Operator
|
|
|
|
|
* \{ */
|
2009-07-02 19:41:31 +00:00
|
|
|
|
|
|
|
|
static int modifier_copy_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2020-07-09 11:44:11 +02:00
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
2024-04-18 17:40:50 +02:00
|
|
|
char name[MAX_NAME];
|
|
|
|
|
RNA_string_get(op->ptr, "modifier", name);
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
bool changed = false;
|
|
|
|
|
for (const PointerRNA &ptr : modifier_get_edit_objects(*C, *op)) {
|
|
|
|
|
Object *ob = static_cast<Object *>(ptr.data);
|
|
|
|
|
ModifierData *md = BKE_modifiers_findby_name(ob, name);
|
|
|
|
|
if (!md) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!modifier_copy(op->reports, bmain, scene, ob, md)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
changed = true;
|
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2024-04-18 17:40:50 +02:00
|
|
|
if (!changed) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
static int modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2021-03-30 09:56:04 -05:00
|
|
|
int retval;
|
2021-04-26 22:16:12 -05:00
|
|
|
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
|
2010-04-22 01:55:10 +00:00
|
|
|
return modifier_copy_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2021-03-30 09:56:04 -05:00
|
|
|
return retval;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
void OBJECT_OT_modifier_copy(wmOperatorType *ot)
|
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Copy Modifier";
|
|
|
|
|
ot->description = "Duplicate modifier at the same position in the stack";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifier_copy";
|
2009-07-02 19:41:31 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->invoke = modifier_copy_invoke;
|
|
|
|
|
ot->exec = modifier_copy_exec;
|
2020-08-12 14:21:40 +02:00
|
|
|
ot->poll = edit_modifier_liboverride_allowed_poll;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-07-02 19:41:31 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2010-04-22 01:55:10 +00:00
|
|
|
edit_modifier_properties(ot);
|
2024-04-18 17:40:50 +02:00
|
|
|
modifier_register_use_selected_objects_prop(ot);
|
2009-07-02 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
2020-12-02 13:35:07 +01:00
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Set Active Modifier Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static int modifier_set_active_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2020-12-02 13:35:07 +01:00
|
|
|
ModifierData *md = edit_modifier_property_get(op, ob, 0);
|
|
|
|
|
|
|
|
|
|
/* If there is no modifier set for this operator, clear the active modifier field. */
|
|
|
|
|
BKE_object_modifier_set_active(ob, md);
|
|
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int modifier_set_active_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
|
|
|
|
int retval;
|
2021-04-26 22:16:12 -05:00
|
|
|
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
|
2020-12-02 13:35:07 +01:00
|
|
|
return modifier_set_active_exec(C, op);
|
|
|
|
|
}
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_modifier_set_active(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Set Active Modifier";
|
|
|
|
|
ot->description = "Activate the modifier to use as the context";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifier_set_active";
|
|
|
|
|
|
|
|
|
|
ot->invoke = modifier_set_active_invoke;
|
|
|
|
|
ot->exec = modifier_set_active_exec;
|
|
|
|
|
ot->poll = edit_modifier_liboverride_allowed_poll;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-28 11:17:49 -06:00
|
|
|
/** \} */
|
2021-12-14 15:49:31 +11:00
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
2020-12-28 11:17:49 -06:00
|
|
|
/** \name Copy Modifier To Selected Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
2024-04-30 15:51:05 -04:00
|
|
|
const Scene *scene = CTX_data_scene(C);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *obact = context_active_object(C);
|
2020-12-28 11:17:49 -06:00
|
|
|
ModifierData *md = edit_modifier_property_get(op, obact, 0);
|
|
|
|
|
if (!md) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int num_copied = 0;
|
|
|
|
|
|
2024-04-30 15:51:05 -04:00
|
|
|
Vector<PointerRNA> selected_objects;
|
|
|
|
|
CTX_data_selected_objects(C, &selected_objects);
|
2020-12-28 11:17:49 -06:00
|
|
|
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
|
|
|
|
|
if (ob == obact) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-09-18 12:47:52 +02:00
|
|
|
if (!ID_IS_EDITABLE(ob)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-04-30 15:51:05 -04:00
|
|
|
if (modifier_copy_to_object(bmain, scene, obact, md, ob, op->reports)) {
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_ADDED, ob);
|
|
|
|
|
num_copied++;
|
2020-12-28 11:17:49 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
|
|
if (num_copied > 0) {
|
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BKE_reportf(op->reports, RPT_ERROR, "Modifier '%s' was not copied to any objects", md->name);
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:56:04 -05:00
|
|
|
static int modifier_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2020-12-28 11:17:49 -06:00
|
|
|
{
|
2021-03-30 09:56:04 -05:00
|
|
|
int retval;
|
2021-04-26 22:16:12 -05:00
|
|
|
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
|
2021-04-19 11:11:48 -05:00
|
|
|
return modifier_copy_to_selected_exec(C, op);
|
2020-12-28 11:17:49 -06:00
|
|
|
}
|
2021-03-30 09:56:04 -05:00
|
|
|
return retval;
|
2020-12-28 11:17:49 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool modifier_copy_to_selected_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : context_active_object(C);
|
2022-04-18 12:40:23 -05:00
|
|
|
ModifierData *md = static_cast<ModifierData *>(ptr.data);
|
2020-12-28 11:17:49 -06:00
|
|
|
|
|
|
|
|
/* This just mirrors the check in #BKE_object_copy_modifier,
|
|
|
|
|
* but there is no reasoning for it there. */
|
|
|
|
|
if (md && ELEM(md->type, eModifierType_Hook, eModifierType_Collision)) {
|
2022-04-18 12:40:23 -05:00
|
|
|
CTX_wm_operator_poll_msg_set(C, R"(Not supported for "Collision" or "Hook" modifiers)");
|
2020-12-28 11:17:49 -06:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!obact) {
|
|
|
|
|
CTX_wm_operator_poll_msg_set(C, "No selected object is active");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!BKE_object_supports_modifiers(obact)) {
|
|
|
|
|
CTX_wm_operator_poll_msg_set(C, "Object type of source object is not supported");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This could have a performance impact in the worst case, where there are many objects selected
|
|
|
|
|
* and none of them pass either of the checks. But that should be uncommon, and this operator is
|
|
|
|
|
* only exposed in a drop-down menu anyway. */
|
|
|
|
|
bool found_supported_objects = false;
|
|
|
|
|
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
|
|
|
|
|
if (ob == obact) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!md && BKE_object_supports_modifiers(ob)) {
|
|
|
|
|
/* Skip type check if modifier could not be found ("modifier" context variable not set). */
|
|
|
|
|
found_supported_objects = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-01-05 17:04:02 +01:00
|
|
|
if (BKE_object_support_modifier_type_check(ob, md->type)) {
|
2020-12-28 11:17:49 -06:00
|
|
|
found_supported_objects = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
|
|
if (!found_supported_objects) {
|
|
|
|
|
CTX_wm_operator_poll_msg_set(C, "No supported objects were selected");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_modifier_copy_to_selected(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Copy Modifier to Selected";
|
|
|
|
|
ot->description = "Copy the modifier from the active object to all selected objects";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifier_copy_to_selected";
|
|
|
|
|
|
|
|
|
|
ot->invoke = modifier_copy_to_selected_invoke;
|
|
|
|
|
ot->exec = modifier_copy_to_selected_exec;
|
|
|
|
|
ot->poll = modifier_copy_to_selected_poll;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 14:15:53 +02:00
|
|
|
static int object_modifiers_copy_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
|
const Scene *scene = CTX_data_scene(C);
|
|
|
|
|
Object *active_object = context_active_object(C);
|
|
|
|
|
|
|
|
|
|
Vector<PointerRNA> selected_objects;
|
|
|
|
|
CTX_data_selected_objects(C, &selected_objects);
|
|
|
|
|
CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
|
|
|
|
|
if (object == active_object) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH (const ModifierData *, md, &active_object->modifiers) {
|
|
|
|
|
if (modifier_copy_to_object(bmain, scene, active_object, md, object, op->reports)) {
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_ADDED, object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
|
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_ADDED, nullptr);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool modifiers_copy_to_selected_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
if (!ED_operator_object_active_editable(C)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-05-02 10:12:46 -04:00
|
|
|
const Object *active_object = context_active_object(C);
|
|
|
|
|
if (!BKE_object_supports_modifiers(active_object)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-05-01 14:15:53 +02:00
|
|
|
if (BLI_listbase_is_empty(&active_object->modifiers)) {
|
|
|
|
|
CTX_wm_operator_poll_msg_set(C, "Active object has no modifiers");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_modifiers_copy_to_selected(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Copy Modifiers to Selected Objects";
|
|
|
|
|
ot->idname = "OBJECT_OT_modifiers_copy_to_selected";
|
|
|
|
|
ot->description = "Copy modifiers to other selected objects";
|
|
|
|
|
|
|
|
|
|
ot->exec = object_modifiers_copy_exec;
|
|
|
|
|
ot->poll = modifiers_copy_to_selected_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-02 13:35:07 +01:00
|
|
|
/** \} */
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Skin Modifier
|
|
|
|
|
* \{ */
|
2012-05-22 15:29:37 +00:00
|
|
|
|
2012-05-26 21:11:23 +00:00
|
|
|
static void modifier_skin_customdata_delete(Object *ob)
|
|
|
|
|
{
|
2023-12-08 16:40:06 -05:00
|
|
|
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
2024-04-18 13:52:20 +02:00
|
|
|
if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
|
2012-05-26 21:11:23 +00:00
|
|
|
BM_data_layer_free(em->bm, &em->bm->vdata, CD_MVERT_SKIN);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2023-12-20 02:21:48 +01:00
|
|
|
CustomData_free_layer_active(&mesh->vert_data, CD_MVERT_SKIN, mesh->verts_num);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2012-05-26 21:11:23 +00:00
|
|
|
}
|
|
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool skin_poll(bContext *C)
|
2012-05-22 15:29:57 +00:00
|
|
|
{
|
2022-10-07 22:52:53 +11:00
|
|
|
return edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH), false, false);
|
2012-05-22 15:29:57 +00:00
|
|
|
}
|
|
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool skin_edit_poll(bContext *C)
|
2012-05-22 15:29:44 +00:00
|
|
|
{
|
2020-06-30 17:51:41 +02:00
|
|
|
Object *ob = CTX_data_edit_object(C);
|
2022-04-18 12:40:23 -05:00
|
|
|
return (ob != nullptr &&
|
2020-08-12 14:21:40 +02:00
|
|
|
edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH), true, false) &&
|
2020-06-30 17:51:41 +02:00
|
|
|
!ID_IS_OVERRIDE_LIBRARY(ob) && !ID_IS_OVERRIDE_LIBRARY(ob->data));
|
2012-05-22 15:29:44 +00:00
|
|
|
}
|
|
|
|
|
|
2016-02-29 08:45:03 +11:00
|
|
|
static void skin_root_clear(BMVert *bm_vert, GSet *visited, const int cd_vert_skin_offset)
|
2012-05-22 15:29:44 +00:00
|
|
|
{
|
|
|
|
|
BMEdge *bm_edge;
|
|
|
|
|
BMIter bm_iter;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-05-22 16:09:31 +00:00
|
|
|
BM_ITER_ELEM (bm_edge, &bm_iter, bm_vert, BM_EDGES_OF_VERT) {
|
2012-05-22 15:29:44 +00:00
|
|
|
BMVert *v2 = BM_edge_other_vert(bm_edge, bm_vert);
|
|
|
|
|
|
2016-02-29 08:45:03 +11:00
|
|
|
if (BLI_gset_add(visited, v2)) {
|
2022-04-18 12:40:23 -05:00
|
|
|
MVertSkin *vs = static_cast<MVertSkin *>(BM_ELEM_CD_GET_VOID_P(v2, cd_vert_skin_offset));
|
2012-05-22 15:29:44 +00:00
|
|
|
|
|
|
|
|
/* clear vertex root flag and add to visited set */
|
|
|
|
|
vs->flag &= ~MVERT_SKIN_ROOT;
|
|
|
|
|
|
2016-02-29 08:45:03 +11:00
|
|
|
skin_root_clear(v2, visited, cd_vert_skin_offset);
|
2012-05-22 15:29:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int skin_root_mark_exec(bContext *C, wmOperator * /*op*/)
|
2012-05-22 15:29:44 +00:00
|
|
|
{
|
2012-05-22 16:24:09 +00:00
|
|
|
Object *ob = CTX_data_edit_object(C);
|
2013-04-16 05:59:48 +00:00
|
|
|
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
2012-08-01 15:04:10 +00:00
|
|
|
BMesh *bm = em->bm;
|
2012-05-22 15:29:44 +00:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
GSet *visited = BLI_gset_ptr_new(__func__);
|
2012-05-22 15:29:44 +00:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
BKE_mesh_ensure_skin_customdata(static_cast<Mesh *>(ob->data));
|
2012-05-22 15:29:44 +00:00
|
|
|
|
2017-03-29 19:32:57 +11:00
|
|
|
const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN);
|
|
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
BMVert *bm_vert;
|
|
|
|
|
BMIter bm_iter;
|
2012-05-22 16:09:31 +00:00
|
|
|
BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) {
|
2016-02-29 08:45:03 +11:00
|
|
|
if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT) && BLI_gset_add(visited, bm_vert)) {
|
2022-04-18 12:40:23 -05:00
|
|
|
MVertSkin *vs = static_cast<MVertSkin *>(
|
|
|
|
|
BM_ELEM_CD_GET_VOID_P(bm_vert, cd_vert_skin_offset));
|
2012-05-22 15:29:44 +00:00
|
|
|
|
|
|
|
|
/* mark vertex as root and add to visited set */
|
|
|
|
|
vs->flag |= MVERT_SKIN_ROOT;
|
|
|
|
|
|
|
|
|
|
/* clear root flag from all connected vertices (recursively) */
|
2016-02-29 08:45:03 +11:00
|
|
|
skin_root_clear(bm_vert, visited, cd_vert_skin_offset);
|
2012-05-22 15:29:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
BLI_gset_free(visited, nullptr);
|
2012-05-22 15:29:44 +00:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2012-05-22 16:24:09 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-05-22 15:29:44 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_skin_root_mark(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Skin Root Mark";
|
|
|
|
|
ot->description = "Mark selected vertices as roots";
|
|
|
|
|
ot->idname = "OBJECT_OT_skin_root_mark";
|
|
|
|
|
|
|
|
|
|
ot->poll = skin_edit_poll;
|
|
|
|
|
ot->exec = skin_root_mark_exec;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-05-22 15:29:44 +00:00
|
|
|
/* flags */
|
2012-05-22 16:24:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2012-05-22 15:29:44 +00:00
|
|
|
}
|
|
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
enum SkinLooseAction {
|
2012-05-22 15:29:44 +00:00
|
|
|
SKIN_LOOSE_MARK,
|
2019-04-16 16:40:47 +02:00
|
|
|
SKIN_LOOSE_CLEAR,
|
2022-04-18 12:40:23 -05:00
|
|
|
};
|
2012-05-22 15:29:44 +00:00
|
|
|
|
|
|
|
|
static int skin_loose_mark_clear_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2012-05-22 16:24:09 +00:00
|
|
|
Object *ob = CTX_data_edit_object(C);
|
2013-04-16 05:59:48 +00:00
|
|
|
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
2012-08-01 15:04:10 +00:00
|
|
|
BMesh *bm = em->bm;
|
2022-04-18 12:40:23 -05:00
|
|
|
SkinLooseAction action = static_cast<SkinLooseAction>(RNA_enum_get(op->ptr, "action"));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 15:04:10 +00:00
|
|
|
if (!CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
BMVert *bm_vert;
|
|
|
|
|
BMIter bm_iter;
|
2012-05-22 16:09:31 +00:00
|
|
|
BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) {
|
2013-05-17 12:43:58 +00:00
|
|
|
if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) {
|
2022-04-18 12:40:23 -05:00
|
|
|
MVertSkin *vs = static_cast<MVertSkin *>(
|
|
|
|
|
CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:44 +00:00
|
|
|
switch (action) {
|
|
|
|
|
case SKIN_LOOSE_MARK:
|
|
|
|
|
vs->flag |= MVERT_SKIN_LOOSE;
|
|
|
|
|
break;
|
|
|
|
|
case SKIN_LOOSE_CLEAR:
|
|
|
|
|
vs->flag &= ~MVERT_SKIN_LOOSE;
|
|
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-05-22 15:29:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2012-05-22 16:24:09 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:44 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_skin_loose_mark_clear(wmOperatorType *ot)
|
|
|
|
|
{
|
2017-10-18 15:07:26 +11:00
|
|
|
static const EnumPropertyItem action_items[] = {
|
2012-05-22 16:24:09 +00:00
|
|
|
{SKIN_LOOSE_MARK, "MARK", 0, "Mark", "Mark selected vertices as loose"},
|
|
|
|
|
{SKIN_LOOSE_CLEAR, "CLEAR", 0, "Clear", "Set selected vertices as not loose"},
|
2022-04-18 12:40:23 -05:00
|
|
|
{0, nullptr, 0, nullptr, nullptr},
|
2012-05-22 16:24:09 +00:00
|
|
|
};
|
2012-05-22 15:29:44 +00:00
|
|
|
|
|
|
|
|
ot->name = "Skin Mark/Clear Loose";
|
|
|
|
|
ot->description = "Mark/clear selected vertices as loose";
|
|
|
|
|
ot->idname = "OBJECT_OT_skin_loose_mark_clear";
|
|
|
|
|
|
|
|
|
|
ot->poll = skin_edit_poll;
|
|
|
|
|
ot->exec = skin_loose_mark_clear_exec;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-05-22 15:29:44 +00:00
|
|
|
/* flags */
|
2012-05-22 16:24:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2012-05-22 15:29:44 +00:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
RNA_def_enum(ot->srna, "action", action_items, SKIN_LOOSE_MARK, "Action", nullptr);
|
2012-05-22 15:29:44 +00:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int skin_radii_equalize_exec(bContext *C, wmOperator * /*op*/)
|
2012-05-22 15:29:44 +00:00
|
|
|
{
|
2012-05-22 16:24:09 +00:00
|
|
|
Object *ob = CTX_data_edit_object(C);
|
2013-04-16 05:59:48 +00:00
|
|
|
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
2012-08-01 15:04:10 +00:00
|
|
|
BMesh *bm = em->bm;
|
2012-05-22 15:29:44 +00:00
|
|
|
|
2012-08-01 15:04:10 +00:00
|
|
|
if (!CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
BMVert *bm_vert;
|
|
|
|
|
BMIter bm_iter;
|
2012-05-22 16:09:31 +00:00
|
|
|
BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) {
|
2013-05-17 12:43:58 +00:00
|
|
|
if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) {
|
2022-04-18 12:40:23 -05:00
|
|
|
MVertSkin *vs = static_cast<MVertSkin *>(
|
|
|
|
|
CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN));
|
2012-05-22 15:29:44 +00:00
|
|
|
float avg = (vs->radius[0] + vs->radius[1]) * 0.5f;
|
|
|
|
|
|
|
|
|
|
vs->radius[0] = vs->radius[1] = avg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2012-05-22 16:24:09 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-05-22 15:29:44 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_skin_radii_equalize(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Skin Radii Equalize";
|
2012-09-12 06:31:26 +00:00
|
|
|
ot->description = "Make skin radii of selected vertices equal on each axis";
|
2012-05-22 15:29:44 +00:00
|
|
|
ot->idname = "OBJECT_OT_skin_radii_equalize";
|
|
|
|
|
|
|
|
|
|
ot->poll = skin_edit_poll;
|
|
|
|
|
ot->exec = skin_radii_equalize_exec;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-05-22 15:29:44 +00:00
|
|
|
/* flags */
|
2012-05-22 16:24:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2012-05-22 15:29:44 +00:00
|
|
|
}
|
|
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
static void skin_armature_bone_create(Object *skin_ob,
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
const Span<float3> positions,
|
2024-03-28 01:30:38 +01:00
|
|
|
const int2 *edges,
|
2018-07-31 10:22:19 +02:00
|
|
|
bArmature *arm,
|
|
|
|
|
BLI_bitmap *edges_visited,
|
2024-03-28 01:30:38 +01:00
|
|
|
const GroupedSpan<int> emap,
|
2018-07-31 10:22:19 +02:00
|
|
|
EditBone *parent_bone,
|
|
|
|
|
int parent_v)
|
2012-05-22 15:29:57 +00:00
|
|
|
{
|
2023-05-24 13:16:57 +02:00
|
|
|
for (int i = 0; i < emap[parent_v].size(); i++) {
|
|
|
|
|
int endx = emap[parent_v][i];
|
2024-03-28 01:30:38 +01:00
|
|
|
const int2 &edge = edges[endx];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
/* ignore edge if already visited */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (BLI_BITMAP_TEST(edges_visited, endx)) {
|
2012-05-22 15:29:57 +00:00
|
|
|
continue;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2014-06-06 16:05:15 +10:00
|
|
|
BLI_BITMAP_ENABLE(edges_visited, endx);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
int v = bke::mesh::edge_other_vert(edge, parent_v);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
EditBone *bone = ED_armature_ebone_add(arm, "Bone");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
bone->parent = parent_bone;
|
2022-04-18 12:40:23 -05:00
|
|
|
if (parent_bone != nullptr) {
|
2020-11-11 18:07:53 +01:00
|
|
|
bone->flag |= BONE_CONNECTED;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
copy_v3_v3(bone->head, positions[parent_v]);
|
|
|
|
|
copy_v3_v3(bone->tail, positions[v]);
|
2012-05-22 15:29:57 +00:00
|
|
|
bone->rad_head = bone->rad_tail = 0.25;
|
2023-05-09 12:50:37 +10:00
|
|
|
SNPRINTF(bone->name, "Bone.%.2d", endx);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
/* add bDeformGroup */
|
2020-10-09 15:08:01 -05:00
|
|
|
bDeformGroup *dg = BKE_object_defgroup_add_name(skin_ob, bone->name);
|
2022-04-18 12:40:23 -05:00
|
|
|
if (dg != nullptr) {
|
2024-03-28 01:30:38 +01:00
|
|
|
blender::ed::object::vgroup_vert_add(skin_ob, dg, parent_v, 1, WEIGHT_REPLACE);
|
|
|
|
|
blender::ed::object::vgroup_vert_add(skin_ob, dg, v, 1, WEIGHT_REPLACE);
|
2012-05-22 15:29:57 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-03-01 15:57:50 -05:00
|
|
|
skin_armature_bone_create(skin_ob, positions, edges, arm, edges_visited, emap, bone, v);
|
2012-05-22 15:29:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-09-07 14:09:50 +10:00
|
|
|
static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, Object *skin_ob)
|
2012-05-22 15:29:57 +00:00
|
|
|
{
|
2023-12-08 16:40:06 -05:00
|
|
|
Mesh *mesh = static_cast<Mesh *>(skin_ob->data);
|
|
|
|
|
const Span<float3> me_positions = mesh->vert_positions();
|
2024-03-28 01:30:38 +01:00
|
|
|
const Span<int2> me_edges = mesh->edges();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-12-01 19:06:44 +03:00
|
|
|
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
|
|
|
|
|
Object *ob_eval = DEG_get_evaluated_object(depsgraph, skin_ob);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-05-20 12:26:07 -04:00
|
|
|
const Mesh *me_eval_deform = blender::bke::mesh_get_eval_deform(
|
Mesh: Remove redundant custom data pointers
For copy-on-write, we want to share attribute arrays between meshes
where possible. Mutable pointers like `Mesh.mvert` make that difficult
by making ownership vague. They also make code more complex by adding
redundancy.
The simplest solution is just removing them and retrieving layers from
`CustomData` as needed. Similar changes have already been applied to
curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of
the pointers generally makes code more obvious and more reusable.
Mesh data is now accessed with a C++ API (`Mesh::edges()` or
`Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`).
The CoW changes this commit makes possible are described in T95845
and T95842, and started in D14139 and D14140. The change also simplifies
the ongoing mesh struct-of-array refactors from T95965.
**RNA/Python Access Performance**
Theoretically, accessing mesh elements with the RNA API may become
slower, since the layer needs to be found on every random access.
However, overhead is already high enough that this doesn't make a
noticible differenc, and performance is actually improved in some
cases. Random access can be up to 10% faster, but other situations
might be a bit slower. Generally using `foreach_get/set` are the best
way to improve performance. See the differential revision for more
discussion about Python performance.
Cycles has been updated to use raw pointers and the internal Blender
mesh types, mostly because there is no sense in having this overhead
when it's already compiled with Blender. In my tests this roughly
halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million
face grid).
Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
|
|
|
depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
const Span<float3> positions_eval = me_eval_deform->vert_positions();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
/* add vertex weights to original mesh */
|
2023-12-20 02:21:48 +01:00
|
|
|
CustomData_add_layer(&mesh->vert_data, CD_MDEFORMVERT, CD_SET_DEFAULT, mesh->verts_num);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-14 21:30:20 +02:00
|
|
|
Scene *scene = DEG_get_input_scene(depsgraph);
|
2018-04-06 12:07:27 +02:00
|
|
|
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
|
2022-09-14 21:30:20 +02:00
|
|
|
Object *arm_ob = BKE_object_add(bmain, scene, view_layer, OB_ARMATURE, nullptr);
|
2012-05-22 15:29:57 +00:00
|
|
|
BKE_object_transform_copy(arm_ob, skin_ob);
|
2022-04-18 12:40:23 -05:00
|
|
|
bArmature *arm = static_cast<bArmature *>(arm_ob->data);
|
Anim: replace Bone Groups & Armature Layers with Bone Collections
Armature layers (the 32 little dots) and bone groups are replaced with
Bone Collections:
- Bone collections are stored on the armature, and have a name that is
unique within that armature.
- An armature can have an arbitrary number of bone collections (instead
of the fixed 32 layers).
- Bones can be assigned to zero or more bone collections.
- Bone collections have a visibility setting, just like objects in scene
collections.
- When a bone is in at least one collection, and all its collections in
are hidden, the bone is hidden. In other cases (in any visible
collection, or in no collection at all), the bone visibility is
determined by its own 'hidden' flag.
- For now, bone collections cannot be nested; they are a flat list just
like bone groups were. Nestability of bone collections is intended to
be implemented in a later 4.x release.
- Since bone collections are defined on the armature, they can be used
from both pose mode and edit mode.
Versioning converts bone groups and armature layers to new bone
collections. Layers that do not contain any bones are skipped. The old
data structures remain in DNA and are unaltered, for limited forward
compatibility. That way at least a save with Blender 4.0 will not
immediately erase the bone group and armature layers and their bone
assignments.
Shortcuts:
- M/Shift+M in pose/edit mode: move to collection (M) and add to
collection (shift+M). This works similar to the M/Shift+M menus for
objects & scene collections.
- Ctrl+G in pose mode shows a port of the old 'bone groups' menu. This
is likely to be removed in the near future, as the functionality
overlaps with the M/Shift+M menus.
This is the first commit of a series; the bone collections feature will
be improved before the Blender 4.0 release. See #108941 for more info.
Pull request: https://projects.blender.org/blender/blender/pulls/109976
2023-08-22 12:15:54 +02:00
|
|
|
ANIM_armature_bonecoll_show_all(arm);
|
2020-07-21 12:53:30 +02:00
|
|
|
arm_ob->dtx |= OB_DRAW_IN_FRONT;
|
2012-05-22 15:29:57 +00:00
|
|
|
arm->drawtype = ARM_LINE;
|
2022-04-18 12:40:23 -05:00
|
|
|
arm->edbo = MEM_cnew<ListBase>("edbo armature");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
MVertSkin *mvert_skin = static_cast<MVertSkin *>(
|
2023-12-20 02:21:48 +01:00
|
|
|
CustomData_get_layer_for_write(&mesh->vert_data, CD_MVERT_SKIN, mesh->verts_num));
|
2023-05-24 13:16:57 +02:00
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
Array<int> vert_to_edge_offsets;
|
|
|
|
|
Array<int> vert_to_edge_indices;
|
|
|
|
|
const GroupedSpan<int> emap = bke::mesh::build_vert_to_edge_map(
|
2023-12-20 02:21:48 +01:00
|
|
|
me_edges, mesh->verts_num, vert_to_edge_offsets, vert_to_edge_indices);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-20 02:21:48 +01:00
|
|
|
BLI_bitmap *edges_visited = BLI_BITMAP_NEW(mesh->edges_num, "edge_visited");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-07-03 23:08:40 +10:00
|
|
|
/* NOTE: we use EditBones here, easier to set them up and use
|
2018-09-02 18:28:27 +10:00
|
|
|
* edit-armature functions to convert back to regular bones */
|
2023-12-20 02:21:48 +01:00
|
|
|
for (int v = 0; v < mesh->verts_num; v++) {
|
2012-05-22 16:24:09 +00:00
|
|
|
if (mvert_skin[v].flag & MVERT_SKIN_ROOT) {
|
2022-04-18 12:40:23 -05:00
|
|
|
EditBone *bone = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
/* Unless the skin root has just one adjacent edge, create
|
2018-09-02 18:28:27 +10:00
|
|
|
* a fake root bone (have it going off in the Y direction
|
|
|
|
|
* (arbitrary) */
|
2023-05-24 13:16:57 +02:00
|
|
|
if (emap[v].size() > 1) {
|
2018-04-15 11:45:51 +02:00
|
|
|
bone = ED_armature_ebone_add(arm, "Bone");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
copy_v3_v3(bone->head, me_positions[v]);
|
|
|
|
|
copy_v3_v3(bone->tail, me_positions[v]);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
bone->head[1] = 1.0f;
|
|
|
|
|
bone->rad_head = bone->rad_tail = 0.25;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-24 13:16:57 +02:00
|
|
|
if (emap[v].size() >= 1) {
|
Mesh: Remove redundant custom data pointers
For copy-on-write, we want to share attribute arrays between meshes
where possible. Mutable pointers like `Mesh.mvert` make that difficult
by making ownership vague. They also make code more complex by adding
redundancy.
The simplest solution is just removing them and retrieving layers from
`CustomData` as needed. Similar changes have already been applied to
curves and point clouds (e9f82d3dc7ee, 410a6efb747f). Removing use of
the pointers generally makes code more obvious and more reusable.
Mesh data is now accessed with a C++ API (`Mesh::edges()` or
`Mesh::edges_for_write()`), and a C API (`BKE_mesh_edges(mesh)`).
The CoW changes this commit makes possible are described in T95845
and T95842, and started in D14139 and D14140. The change also simplifies
the ongoing mesh struct-of-array refactors from T95965.
**RNA/Python Access Performance**
Theoretically, accessing mesh elements with the RNA API may become
slower, since the layer needs to be found on every random access.
However, overhead is already high enough that this doesn't make a
noticible differenc, and performance is actually improved in some
cases. Random access can be up to 10% faster, but other situations
might be a bit slower. Generally using `foreach_get/set` are the best
way to improve performance. See the differential revision for more
discussion about Python performance.
Cycles has been updated to use raw pointers and the internal Blender
mesh types, mostly because there is no sense in having this overhead
when it's already compiled with Blender. In my tests this roughly
halves the Cycles mesh creation time (0.19s to 0.10s for a 1 million
face grid).
Differential Revision: https://developer.blender.org/D15488
2022-09-05 11:56:34 -05:00
|
|
|
skin_armature_bone_create(
|
Mesh: Move positions to a generic attribute
**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
2023-01-10 00:10:43 -05:00
|
|
|
skin_ob, positions_eval, me_edges.data(), arm, edges_visited, emap, bone, v);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-05-22 15:29:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
MEM_freeN(edges_visited);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-06 15:50:24 +02:00
|
|
|
ED_armature_from_edit(bmain, arm);
|
2013-12-17 22:13:15 +11:00
|
|
|
ED_armature_edit_free(arm);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
return arm_ob;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int skin_armature_create_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2020-10-09 15:08:01 -05:00
|
|
|
Object *ob = CTX_data_active_object(C);
|
2023-12-08 16:40:06 -05:00
|
|
|
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
2012-05-22 15:29:57 +00:00
|
|
|
ModifierData *skin_md;
|
|
|
|
|
|
2023-12-08 16:40:06 -05:00
|
|
|
if (!CustomData_has_layer(&mesh->vert_data, CD_MVERT_SKIN)) {
|
|
|
|
|
BKE_reportf(op->reports, RPT_WARNING, "Mesh '%s' has no skin vertex data", mesh->id.name + 2);
|
2012-08-01 14:37:13 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
/* create new armature */
|
2020-10-09 15:08:01 -05:00
|
|
|
Object *arm_ob = modifier_skin_armature_create(depsgraph, bmain, ob);
|
2012-05-22 15:29:57 +00:00
|
|
|
|
|
|
|
|
/* add a modifier to connect the new armature to the mesh */
|
2020-10-09 15:08:01 -05:00
|
|
|
ArmatureModifierData *arm_md = (ArmatureModifierData *)BKE_modifier_new(eModifierType_Armature);
|
2012-05-22 15:29:57 +00:00
|
|
|
if (arm_md) {
|
|
|
|
|
skin_md = edit_modifier_property_get(op, ob, eModifierType_Skin);
|
|
|
|
|
BLI_insertlinkafter(&ob->modifiers, skin_md, arm_md);
|
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(*arm_ob, arm_md->modifier);
|
2012-05-22 15:29:57 +00:00
|
|
|
|
|
|
|
|
arm_md->object = arm_ob;
|
|
|
|
|
arm_md->deformflag = ARM_DEF_VGROUP | ARM_DEF_QUATERNION;
|
2017-06-08 10:14:53 +02:00
|
|
|
DEG_relations_tag_update(bmain);
|
2018-12-06 17:52:37 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2012-05-22 15:29:57 +00:00
|
|
|
}
|
|
|
|
|
|
2012-05-22 16:24:09 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
2012-05-22 15:29:57 +00:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int skin_armature_create_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2012-05-22 15:29:57 +00:00
|
|
|
{
|
2020-12-02 13:35:07 +01:00
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
2012-05-22 15:29:57 +00:00
|
|
|
return skin_armature_create_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:42:22 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2012-05-22 15:29:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_skin_armature_create(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Skin Armature Create";
|
|
|
|
|
ot->description = "Create an armature that parallels the skin layout";
|
|
|
|
|
ot->idname = "OBJECT_OT_skin_armature_create";
|
|
|
|
|
|
|
|
|
|
ot->poll = skin_poll;
|
|
|
|
|
ot->invoke = skin_armature_create_invoke;
|
|
|
|
|
ot->exec = skin_armature_create_exec;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-05-22 15:29:57 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2012-05-22 15:29:57 +00:00
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
}
|
2021-12-14 15:49:31 +11:00
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Delta Mesh Bind Operator
|
|
|
|
|
* \{ */
|
2015-03-29 04:44:05 +11:00
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool correctivesmooth_poll(bContext *C)
|
2015-03-29 04:44:05 +11:00
|
|
|
{
|
2020-08-12 14:21:40 +02:00
|
|
|
return edit_modifier_poll_generic(C, &RNA_CorrectiveSmoothModifier, 0, true, false);
|
2015-03-29 04:44:05 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int correctivesmooth_bind_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2015-03-29 04:44:05 +11:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2015-03-29 04:44:05 +11:00
|
|
|
CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)edit_modifier_property_get(
|
|
|
|
|
op, ob, eModifierType_CorrectiveSmooth);
|
|
|
|
|
|
|
|
|
|
if (!csmd) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-08 10:14:02 +02:00
|
|
|
if (!BKE_modifier_is_enabled(scene, &csmd->modifier, eModifierMode_Realtime)) {
|
2015-03-29 04:44:05 +11:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "Modifier is disabled");
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
const bool is_bind = (csmd->bind_coords != nullptr);
|
2015-03-29 04:44:05 +11:00
|
|
|
|
|
|
|
|
MEM_SAFE_FREE(csmd->bind_coords);
|
2019-09-25 16:45:05 +02:00
|
|
|
MEM_SAFE_FREE(csmd->delta_cache.deltas);
|
2015-03-29 04:44:05 +11:00
|
|
|
|
|
|
|
|
if (is_bind) {
|
|
|
|
|
/* toggle off */
|
|
|
|
|
csmd->bind_coords_num = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-04-04 14:42:33 +02:00
|
|
|
/* Signal to modifier to recalculate. */
|
|
|
|
|
CorrectiveSmoothModifierData *csmd_eval = (CorrectiveSmoothModifierData *)
|
2020-05-08 10:14:02 +02:00
|
|
|
BKE_modifier_get_evaluated(depsgraph, ob, &csmd->modifier);
|
2022-09-25 18:33:28 +10:00
|
|
|
csmd_eval->bind_coords_num = uint(-1);
|
2019-01-02 11:26:31 +01:00
|
|
|
|
2019-01-15 23:24:20 +11:00
|
|
|
/* Force modifier to run, it will call binding routine
|
|
|
|
|
* (this has to happen outside of depsgraph evaluation). */
|
2019-04-04 14:42:33 +02:00
|
|
|
object_force_modifier_bind_simple_options(depsgraph, ob, &csmd->modifier);
|
2015-03-29 04:44:05 +11:00
|
|
|
}
|
|
|
|
|
|
2019-04-04 14:42:33 +02:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2015-03-29 04:44:05 +11:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int correctivesmooth_bind_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2015-03-29 04:44:05 +11:00
|
|
|
{
|
2020-12-02 13:35:07 +01:00
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
2015-03-29 04:44:05 +11:00
|
|
|
return correctivesmooth_bind_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:42:22 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2015-03-29 04:44:05 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_correctivesmooth_bind(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Corrective Smooth Bind";
|
2015-04-06 20:43:34 +02:00
|
|
|
ot->description = "Bind base pose in Corrective Smooth modifier";
|
2015-03-29 04:44:05 +11:00
|
|
|
ot->idname = "OBJECT_OT_correctivesmooth_bind";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
|
|
|
|
ot->poll = correctivesmooth_poll;
|
|
|
|
|
ot->invoke = correctivesmooth_bind_invoke;
|
|
|
|
|
ot->exec = correctivesmooth_bind_exec;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
}
|
2012-05-22 15:29:37 +00:00
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Mesh Deform Bind Operator
|
|
|
|
|
* \{ */
|
2009-06-03 00:09:30 +00:00
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool meshdeform_poll(bContext *C)
|
2009-06-03 00:09:30 +00:00
|
|
|
{
|
2020-08-12 14:21:40 +02:00
|
|
|
return edit_modifier_poll_generic(C, &RNA_MeshDeformModifier, 0, true, false);
|
2009-06-03 00:09:30 +00:00
|
|
|
}
|
|
|
|
|
|
2009-08-21 02:51:56 +00:00
|
|
|
static int meshdeform_bind_exec(bContext *C, wmOperator *op)
|
2009-06-03 00:09:30 +00:00
|
|
|
{
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2010-10-16 14:32:17 +00:00
|
|
|
MeshDeformModifierData *mmd = (MeshDeformModifierData *)edit_modifier_property_get(
|
|
|
|
|
op, ob, eModifierType_MeshDeform);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if (mmd == nullptr) {
|
2010-04-22 01:55:10 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2018-12-07 11:17:25 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if (mmd->bindcagecos != nullptr) {
|
2018-12-07 11:17:25 +01:00
|
|
|
MEM_SAFE_FREE(mmd->bindcagecos);
|
|
|
|
|
MEM_SAFE_FREE(mmd->dyngrid);
|
|
|
|
|
MEM_SAFE_FREE(mmd->dyninfluences);
|
|
|
|
|
MEM_SAFE_FREE(mmd->bindinfluences);
|
|
|
|
|
MEM_SAFE_FREE(mmd->bindoffsets);
|
|
|
|
|
MEM_SAFE_FREE(mmd->dynverts);
|
|
|
|
|
MEM_SAFE_FREE(mmd->bindweights); /* Deprecated */
|
|
|
|
|
MEM_SAFE_FREE(mmd->bindcos); /* Deprecated */
|
2022-03-28 12:29:47 +11:00
|
|
|
mmd->verts_num = 0;
|
|
|
|
|
mmd->cage_verts_num = 0;
|
|
|
|
|
mmd->influences_num = 0;
|
2009-06-03 00:09:30 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2019-04-22 00:18:34 +10:00
|
|
|
/* Force modifier to run, it will call binding routine
|
|
|
|
|
* (this has to happen outside of depsgraph evaluation). */
|
2020-05-08 10:14:02 +02:00
|
|
|
MeshDeformModifierData *mmd_eval = (MeshDeformModifierData *)BKE_modifier_get_evaluated(
|
2019-04-04 14:42:33 +02:00
|
|
|
depsgraph, ob, &mmd->modifier);
|
|
|
|
|
mmd_eval->bindfunc = ED_mesh_deform_bind_callback;
|
|
|
|
|
object_force_modifier_bind_simple_options(depsgraph, ob, &mmd->modifier);
|
2022-04-18 12:40:23 -05:00
|
|
|
mmd_eval->bindfunc = nullptr;
|
2009-06-03 00:09:30 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-04 14:42:33 +02:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2018-09-26 12:46:34 +02:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
2009-06-03 00:09:30 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int meshdeform_bind_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2020-12-02 13:35:07 +01:00
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
2010-04-22 01:55:10 +00:00
|
|
|
return meshdeform_bind_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:42:22 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2009-08-21 02:51:56 +00:00
|
|
|
void OBJECT_OT_meshdeform_bind(wmOperatorType *ot)
|
2009-06-03 00:09:30 +00:00
|
|
|
{
|
|
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Mesh Deform Bind";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description = "Bind mesh to cage in mesh deform modifier";
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->idname = "OBJECT_OT_meshdeform_bind";
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-06-03 00:09:30 +00:00
|
|
|
/* api callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->poll = meshdeform_poll;
|
|
|
|
|
ot->invoke = meshdeform_bind_invoke;
|
|
|
|
|
ot->exec = meshdeform_bind_exec;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-06-03 00:09:30 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2010-04-22 01:55:10 +00:00
|
|
|
edit_modifier_properties(ot);
|
2009-06-03 00:09:30 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Explode Refresh Operator
|
|
|
|
|
* \{ */
|
2009-08-21 02:51:56 +00:00
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool explode_poll(bContext *C)
|
2009-06-03 00:09:30 +00:00
|
|
|
{
|
2020-08-12 14:21:40 +02:00
|
|
|
return edit_modifier_poll_generic(C, &RNA_ExplodeModifier, 0, true, false);
|
2009-06-03 00:09:30 +00:00
|
|
|
}
|
|
|
|
|
|
2009-08-21 02:51:56 +00:00
|
|
|
static int explode_refresh_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2010-10-16 14:32:17 +00:00
|
|
|
ExplodeModifierData *emd = (ExplodeModifierData *)edit_modifier_property_get(
|
|
|
|
|
op, ob, eModifierType_Explode);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!emd) {
|
2010-04-22 01:55:10 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2009-08-21 02:51:56 +00:00
|
|
|
|
|
|
|
|
emd->flag |= eExplodeFlag_CalcFaces;
|
|
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
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);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-08-21 02:51:56 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int explode_refresh_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2010-04-22 01:55:10 +00:00
|
|
|
{
|
2020-12-02 13:35:07 +01:00
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
2010-04-22 01:55:10 +00:00
|
|
|
return explode_refresh_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:42:22 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2010-04-22 01:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
2009-08-21 02:51:56 +00:00
|
|
|
void OBJECT_OT_explode_refresh(wmOperatorType *ot)
|
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Explode Refresh";
|
|
|
|
|
ot->description = "Refresh data in the Explode modifier";
|
|
|
|
|
ot->idname = "OBJECT_OT_explode_refresh";
|
2009-08-21 02:51:56 +00:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->poll = explode_poll;
|
|
|
|
|
ot->invoke = explode_refresh_invoke;
|
|
|
|
|
ot->exec = explode_refresh_exec;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2009-08-21 02:51:56 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2010-04-22 01:55:10 +00:00
|
|
|
edit_modifier_properties(ot);
|
2009-08-21 02:51:56 +00:00
|
|
|
}
|
2009-06-03 00:09:30 +00:00
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Ocean Bake Operator
|
|
|
|
|
* \{ */
|
2011-11-13 12:17:27 +00:00
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool ocean_bake_poll(bContext *C)
|
2011-11-13 12:17:27 +00:00
|
|
|
{
|
2020-08-12 14:21:40 +02:00
|
|
|
return edit_modifier_poll_generic(C, &RNA_OceanModifier, 0, true, false);
|
2011-11-13 12:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
struct OceanBakeJob {
|
2011-11-13 12:17:27 +00:00
|
|
|
/* from wmJob */
|
2023-06-03 08:36:28 +10:00
|
|
|
Object *owner;
|
2022-11-04 18:37:25 +11:00
|
|
|
bool *stop, *do_update;
|
2011-11-13 12:17:27 +00:00
|
|
|
float *progress;
|
|
|
|
|
int current_frame;
|
2023-06-03 08:36:28 +10:00
|
|
|
OceanCache *och;
|
|
|
|
|
Ocean *ocean;
|
|
|
|
|
OceanModifierData *omd;
|
2022-04-18 12:40:23 -05:00
|
|
|
};
|
2011-11-13 12:17:27 +00:00
|
|
|
|
|
|
|
|
static void oceanbake_free(void *customdata)
|
|
|
|
|
{
|
2022-04-18 12:40:23 -05:00
|
|
|
OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata);
|
|
|
|
|
MEM_delete(oj);
|
2011-11-13 12:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* called by oceanbake, only to check job 'stop' value */
|
2022-10-03 17:37:25 -05:00
|
|
|
static int oceanbake_breakjob(void * /*customdata*/)
|
2011-11-13 12:17:27 +00:00
|
|
|
{
|
2019-05-01 11:09:22 +10:00
|
|
|
// OceanBakeJob *ob = (OceanBakeJob *)customdata;
|
|
|
|
|
// return *(ob->stop);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
|
|
|
|
/* this is not nice yet, need to make the jobs list template better
|
2018-09-02 18:28:27 +10:00
|
|
|
* for identifying/acting upon various different jobs */
|
2011-11-13 12:17:27 +00:00
|
|
|
/* but for now we'll reuse the render break... */
|
2012-08-08 18:37:06 +00:00
|
|
|
return (G.is_break);
|
2011-11-13 12:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* called by oceanbake, wmJob sends notifier */
|
|
|
|
|
static void oceanbake_update(void *customdata, float progress, int *cancel)
|
|
|
|
|
{
|
2022-04-18 12:40:23 -05:00
|
|
|
OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (oceanbake_breakjob(oj)) {
|
2011-11-13 12:17:27 +00:00
|
|
|
*cancel = 1;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
*(oj->do_update) = true;
|
2012-04-28 15:42:27 +00:00
|
|
|
*(oj->progress) = progress;
|
2011-11-13 12:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-06 13:06:02 +02:00
|
|
|
static void oceanbake_startjob(void *customdata, wmJobWorkerStatus *worker_status)
|
2011-11-13 12:17:27 +00:00
|
|
|
{
|
2022-04-18 12:40:23 -05:00
|
|
|
OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2023-10-06 13:06:02 +02:00
|
|
|
oj->stop = &worker_status->stop;
|
|
|
|
|
oj->do_update = &worker_status->do_update;
|
|
|
|
|
oj->progress = &worker_status->progress;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
G.is_break = false; /* XXX shared with render - replace with job 'stop' switch */
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2015-04-04 15:18:52 +11:00
|
|
|
BKE_ocean_bake(oj->ocean, oj->och, oceanbake_update, (void *)oj);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2023-10-06 13:06:02 +02:00
|
|
|
worker_status->do_update = true;
|
|
|
|
|
worker_status->stop = false;
|
2011-11-13 12:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void oceanbake_endjob(void *customdata)
|
|
|
|
|
{
|
2022-04-18 12:40:23 -05:00
|
|
|
OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
if (oj->ocean) {
|
2015-04-04 15:18:52 +11:00
|
|
|
BKE_ocean_free(oj->ocean);
|
2022-04-18 12:40:23 -05:00
|
|
|
oj->ocean = nullptr;
|
2011-11-13 12:17:27 +00:00
|
|
|
}
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
oj->omd->oceancache = oj->och;
|
2014-04-01 11:34:00 +11:00
|
|
|
oj->omd->cached = true;
|
2018-07-05 16:23:35 +02:00
|
|
|
|
|
|
|
|
Object *ob = oj->owner;
|
2024-02-19 15:54:08 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_SYNC_TO_EVAL);
|
2011-11-13 12:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ocean_bake_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2018-05-31 15:24:30 +02:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2011-11-13 12:17:27 +00:00
|
|
|
OceanModifierData *omd = (OceanModifierData *)edit_modifier_property_get(
|
|
|
|
|
op, ob, eModifierType_Ocean);
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
2014-02-03 18:55:59 +11:00
|
|
|
const bool free = RNA_boolean_get(op->ptr, "free");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!omd) {
|
2011-11-13 12:17:27 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
if (free) {
|
2018-07-05 16:23:35 +02:00
|
|
|
BKE_ocean_free_modifier_cache(omd);
|
2019-04-04 14:42:33 +02:00
|
|
|
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);
|
2011-11-13 12:17:27 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
OceanCache *och = BKE_ocean_init_cache(omd->cachepath,
|
|
|
|
|
BKE_modifier_path_relbase(bmain, ob),
|
|
|
|
|
omd->bakestart,
|
|
|
|
|
omd->bakeend,
|
|
|
|
|
omd->wave_scale,
|
|
|
|
|
omd->chop_amount,
|
|
|
|
|
omd->foam_coverage,
|
|
|
|
|
omd->foam_fade,
|
|
|
|
|
omd->resolution);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
och->time = static_cast<float *>(MEM_mallocN(och->duration * sizeof(float), "foam bake time"));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-10-09 15:08:01 -05:00
|
|
|
int cfra = scene->r.cfra;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
/* precalculate time variable before baking */
|
2020-10-09 15:08:01 -05:00
|
|
|
int i = 0;
|
T77086 Animation: Passing Dependency Graph to Drivers
Custom driver functions need access to the dependency graph that is
triggering the evaluation of the driver. This patch passes the
dependency graph pointer through all the animation-related calls.
Instead of passing the evaluation time to functions, the code now passes
an `AnimationEvalContext` pointer:
```
typedef struct AnimationEvalContext {
struct Depsgraph *const depsgraph;
const float eval_time;
} AnimationEvalContext;
```
These structs are read-only, meaning that the code cannot change the
evaluation time. Note that the `depsgraph` pointer itself is const, but
it points to a non-const depsgraph.
FCurves and Drivers can be evaluated at a different time than the
current scene time, for example when evaluating NLA strips. This means
that, even though the current time is stored in the dependency graph, we
need an explicit evaluation time.
There are two functions that allow creation of `AnimationEvalContext`
objects:
- `BKE_animsys_eval_context_construct(Depsgraph *depsgraph, float
eval_time)`, which creates a new context object from scratch, and
- `BKE_animsys_eval_context_construct_at(AnimationEvalContext
*anim_eval_context, float eval_time)`, which can be used to create a
`AnimationEvalContext` with the same depsgraph, but at a different
time. This makes it possible to later add fields without changing any
of the code that just want to change the eval time.
This also provides a fix for T75553, although it does require a change
to the custom driver function. The driver should call
`custom_function(depsgraph)`, and the function should use that depsgraph
instead of information from `bpy.context`.
Reviewed By: brecht, sergey
Differential Revision: https://developer.blender.org/D8047
2020-07-17 17:38:09 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
2020-10-09 15:08:01 -05:00
|
|
|
for (int f = omd->bakestart; f <= omd->bakeend; f++) {
|
2019-01-29 11:59:17 +01:00
|
|
|
/* For now only simple animation of time value is supported, nothing else.
|
|
|
|
|
* No drivers or other modifier parameters. */
|
2019-07-31 14:56:17 +02:00
|
|
|
/* TODO(sergey): This operates on an original data, so no flush is needed. However, baking
|
|
|
|
|
* usually should happen on an evaluated objects, so this seems to be deeper issue here. */
|
T77086 Animation: Passing Dependency Graph to Drivers
Custom driver functions need access to the dependency graph that is
triggering the evaluation of the driver. This patch passes the
dependency graph pointer through all the animation-related calls.
Instead of passing the evaluation time to functions, the code now passes
an `AnimationEvalContext` pointer:
```
typedef struct AnimationEvalContext {
struct Depsgraph *const depsgraph;
const float eval_time;
} AnimationEvalContext;
```
These structs are read-only, meaning that the code cannot change the
evaluation time. Note that the `depsgraph` pointer itself is const, but
it points to a non-const depsgraph.
FCurves and Drivers can be evaluated at a different time than the
current scene time, for example when evaluating NLA strips. This means
that, even though the current time is stored in the dependency graph, we
need an explicit evaluation time.
There are two functions that allow creation of `AnimationEvalContext`
objects:
- `BKE_animsys_eval_context_construct(Depsgraph *depsgraph, float
eval_time)`, which creates a new context object from scratch, and
- `BKE_animsys_eval_context_construct_at(AnimationEvalContext
*anim_eval_context, float eval_time)`, which can be used to create a
`AnimationEvalContext` with the same depsgraph, but at a different
time. This makes it possible to later add fields without changing any
of the code that just want to change the eval time.
This also provides a fix for T75553, although it does require a change
to the custom driver function. The driver should call
`custom_function(depsgraph)`, and the function should use that depsgraph
instead of information from `bpy.context`.
Reviewed By: brecht, sergey
Differential Revision: https://developer.blender.org/D8047
2020-07-17 17:38:09 +02:00
|
|
|
|
|
|
|
|
const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
|
|
|
|
|
f);
|
|
|
|
|
BKE_animsys_evaluate_animdata((ID *)ob, ob->adt, &anim_eval_context, ADT_RECALC_ANIM, false);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
och->time[i] = omd->time;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-02-05 16:23:34 +11:00
|
|
|
/* Make a copy of ocean to use for baking - thread-safety. */
|
2023-06-03 08:36:28 +10:00
|
|
|
Ocean *ocean = BKE_ocean_add();
|
2020-08-03 10:08:31 -04:00
|
|
|
BKE_ocean_init_from_modifier(ocean, omd, omd->resolution);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-03-03 16:31:46 +00:00
|
|
|
#if 0
|
2015-04-04 15:18:52 +11:00
|
|
|
BKE_ocean_bake(ocean, och);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
omd->oceancache = och;
|
2014-04-01 11:34:00 +11:00
|
|
|
omd->cached = true;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
scene->r.cfra = cfra;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2018-12-06 17:52:37 +01:00
|
|
|
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);
|
2012-03-03 16:31:46 +00:00
|
|
|
#endif
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
/* job stuff */
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
scene->r.cfra = cfra;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
/* setup job */
|
2020-10-09 15:08:01 -05:00
|
|
|
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
|
|
|
|
|
CTX_wm_window(C),
|
|
|
|
|
scene,
|
|
|
|
|
"Ocean Simulation",
|
|
|
|
|
WM_JOB_PROGRESS,
|
|
|
|
|
WM_JOB_TYPE_OBJECT_SIM_OCEAN);
|
2022-04-18 12:40:23 -05:00
|
|
|
OceanBakeJob *oj = MEM_cnew<OceanBakeJob>("ocean bake job");
|
2018-07-05 16:23:35 +02:00
|
|
|
oj->owner = ob;
|
2011-11-13 12:17:27 +00:00
|
|
|
oj->ocean = ocean;
|
|
|
|
|
oj->och = och;
|
|
|
|
|
oj->omd = omd;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-15 10:03:29 +00:00
|
|
|
WM_jobs_customdata_set(wm_job, oj, oceanbake_free);
|
|
|
|
|
WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
|
2022-04-18 12:40:23 -05:00
|
|
|
WM_jobs_callbacks(wm_job, oceanbake_startjob, nullptr, nullptr, oceanbake_endjob);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-15 10:03:29 +00:00
|
|
|
WM_jobs_start(CTX_wm_manager(C), wm_job);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int ocean_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2011-11-13 12:17:27 +00:00
|
|
|
{
|
2020-12-02 13:35:07 +01:00
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
2011-11-13 12:17:27 +00:00
|
|
|
return ocean_bake_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:42:22 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2011-11-13 12:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_ocean_bake(wmOperatorType *ot)
|
|
|
|
|
{
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Bake Ocean";
|
|
|
|
|
ot->description = "Bake an image sequence of ocean data";
|
|
|
|
|
ot->idname = "OBJECT_OT_ocean_bake";
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->poll = ocean_bake_poll;
|
|
|
|
|
ot->invoke = ocean_bake_invoke;
|
|
|
|
|
ot->exec = ocean_bake_exec;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2011-11-13 12:17:27 +00:00
|
|
|
/* flags */
|
2012-06-29 23:09:19 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
2011-11-13 12:17:27 +00:00
|
|
|
edit_modifier_properties(ot);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
RNA_def_boolean(ot->srna, "free", false, "Free", "Free the bake, rather than generating it");
|
2011-11-13 12:17:27 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
2021-09-01 21:41:23 +10:00
|
|
|
/** \name Laplacian-Deform Bind Operator
|
2020-03-26 10:24:44 +11:00
|
|
|
* \{ */
|
2013-11-24 07:00:49 +11:00
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool laplaciandeform_poll(bContext *C)
|
2013-11-24 07:00:49 +11:00
|
|
|
{
|
2020-08-12 14:21:40 +02:00
|
|
|
return edit_modifier_poll_generic(C, &RNA_LaplacianDeformModifier, 0, false, false);
|
2013-11-24 07:00:49 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int laplaciandeform_bind_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2013-11-24 07:00:49 +11:00
|
|
|
LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)edit_modifier_property_get(
|
|
|
|
|
op, ob, eModifierType_LaplacianDeform);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if (lmd == nullptr) {
|
2013-11-24 07:00:49 +11:00
|
|
|
return OPERATOR_CANCELLED;
|
2018-12-05 18:22:18 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-11-24 07:00:49 +11:00
|
|
|
if (lmd->flag & MOD_LAPLACIANDEFORM_BIND) {
|
|
|
|
|
lmd->flag &= ~MOD_LAPLACIANDEFORM_BIND;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
lmd->flag |= MOD_LAPLACIANDEFORM_BIND;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-05-08 19:02:03 +10:00
|
|
|
LaplacianDeformModifierData *lmd_eval = (LaplacianDeformModifierData *)
|
|
|
|
|
BKE_modifier_get_evaluated(depsgraph, ob, &lmd->modifier);
|
2019-04-04 14:42:33 +02:00
|
|
|
lmd_eval->flag = lmd->flag;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-01-15 23:24:20 +11:00
|
|
|
/* Force modifier to run, it will call binding routine
|
|
|
|
|
* (this has to happen outside of depsgraph evaluation). */
|
2019-04-04 14:42:33 +02:00
|
|
|
object_force_modifier_bind_simple_options(depsgraph, ob, &lmd->modifier);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-04 14:42:33 +02:00
|
|
|
/* This is hard to know from the modifier itself whether the evaluation is
|
|
|
|
|
* happening for binding or not. So we copy all the required data here. */
|
2022-03-28 12:29:47 +11:00
|
|
|
lmd->verts_num = lmd_eval->verts_num;
|
2022-04-18 12:40:23 -05:00
|
|
|
if (lmd_eval->vertexco == nullptr) {
|
2019-04-04 14:42:33 +02:00
|
|
|
MEM_SAFE_FREE(lmd->vertexco);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-04-18 12:40:23 -05:00
|
|
|
lmd->vertexco = static_cast<float *>(MEM_dupallocN(lmd_eval->vertexco));
|
2019-04-04 14:42:33 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-04 14:42:33 +02:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2013-11-24 07:00:49 +11:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int laplaciandeform_bind_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2013-11-24 07:00:49 +11:00
|
|
|
{
|
2020-12-02 13:35:07 +01:00
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
2013-11-24 07:00:49 +11:00
|
|
|
return laplaciandeform_bind_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:42:22 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2013-11-24 07:00:49 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_laplaciandeform_bind(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Laplacian Deform Bind";
|
|
|
|
|
ot->description = "Bind mesh to system in laplacian deform modifier";
|
|
|
|
|
ot->idname = "OBJECT_OT_laplaciandeform_bind";
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2013-11-24 07:00:49 +11:00
|
|
|
/* api callbacks */
|
|
|
|
|
ot->poll = laplaciandeform_poll;
|
|
|
|
|
ot->invoke = laplaciandeform_bind_invoke;
|
|
|
|
|
ot->exec = laplaciandeform_bind_exec;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2013-11-24 07:00:49 +11:00
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
}
|
2017-02-27 12:39:14 -03:00
|
|
|
|
2020-03-26 10:24:44 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Surface Deform Bind Operator
|
|
|
|
|
* \{ */
|
2017-02-27 12:39:14 -03:00
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool surfacedeform_bind_poll(bContext *C)
|
2017-02-27 12:39:14 -03:00
|
|
|
{
|
2020-08-12 14:21:40 +02:00
|
|
|
return edit_modifier_poll_generic(C, &RNA_SurfaceDeformModifier, 0, true, false);
|
2017-02-27 12:39:14 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int surfacedeform_bind_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2019-07-25 16:36:22 +02:00
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
2017-02-27 12:39:14 -03:00
|
|
|
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)edit_modifier_property_get(
|
|
|
|
|
op, ob, eModifierType_SurfaceDeform);
|
|
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if (smd == nullptr) {
|
2017-02-27 12:39:14 -03:00
|
|
|
return OPERATOR_CANCELLED;
|
2018-12-05 18:22:18 +01:00
|
|
|
}
|
|
|
|
|
|
2017-02-27 12:39:14 -03:00
|
|
|
if (smd->flags & MOD_SDEF_BIND) {
|
|
|
|
|
smd->flags &= ~MOD_SDEF_BIND;
|
|
|
|
|
}
|
|
|
|
|
else if (smd->target) {
|
|
|
|
|
smd->flags |= MOD_SDEF_BIND;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-08 10:14:02 +02:00
|
|
|
SurfaceDeformModifierData *smd_eval = (SurfaceDeformModifierData *)BKE_modifier_get_evaluated(
|
2019-04-04 14:42:33 +02:00
|
|
|
depsgraph, ob, &smd->modifier);
|
|
|
|
|
smd_eval->flags = smd->flags;
|
|
|
|
|
|
2019-01-15 23:24:20 +11:00
|
|
|
/* Force modifier to run, it will call binding routine
|
|
|
|
|
* (this has to happen outside of depsgraph evaluation). */
|
2019-04-04 14:42:33 +02:00
|
|
|
object_force_modifier_bind_simple_options(depsgraph, ob, &smd->modifier);
|
2018-12-05 18:22:18 +01:00
|
|
|
|
2019-04-04 14:42:33 +02:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2017-02-27 12:39:14 -03:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int surfacedeform_bind_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2017-02-27 12:39:14 -03:00
|
|
|
{
|
2020-12-02 13:35:07 +01:00
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
2017-02-27 12:39:14 -03:00
|
|
|
return surfacedeform_bind_exec(C, op);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2020-07-03 15:42:22 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2017-02-27 12:39:14 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Surface Deform Bind";
|
|
|
|
|
ot->description = "Bind mesh to target in surface deform modifier";
|
|
|
|
|
ot->idname = "OBJECT_OT_surfacedeform_bind";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
|
|
|
|
ot->poll = surfacedeform_bind_poll;
|
|
|
|
|
ot->invoke = surfacedeform_bind_invoke;
|
|
|
|
|
ot->exec = surfacedeform_bind_exec;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
}
|
2020-03-26 10:24:44 +11:00
|
|
|
|
|
|
|
|
/** \} */
|
2021-09-16 20:49:10 -05:00
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Toggle Value or Attribute Operator
|
|
|
|
|
*
|
|
|
|
|
* \note This operator basically only exists to provide a better tooltip for the toggle button,
|
|
|
|
|
* since it is stored as an IDProperty. It also stops the button from being highlighted when
|
|
|
|
|
* "use_attribute" is on, which isn't expected.
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static int geometry_nodes_input_attribute_toggle_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2021-09-16 20:49:10 -05:00
|
|
|
|
|
|
|
|
char modifier_name[MAX_NAME];
|
|
|
|
|
RNA_string_get(op->ptr, "modifier_name", modifier_name);
|
|
|
|
|
NodesModifierData *nmd = (NodesModifierData *)BKE_modifiers_findby_name(ob, modifier_name);
|
2022-04-18 12:40:23 -05:00
|
|
|
if (nmd == nullptr) {
|
2021-09-16 20:49:10 -05:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-24 13:36:25 +02:00
|
|
|
char input_name[MAX_NAME];
|
|
|
|
|
RNA_string_get(op->ptr, "input_name", input_name);
|
2021-09-16 20:49:10 -05:00
|
|
|
|
2023-08-24 13:36:25 +02:00
|
|
|
IDProperty *use_attribute = IDP_GetPropertyFromGroup(
|
|
|
|
|
nmd->settings.properties, std::string(input_name + std::string("_use_attribute")).c_str());
|
|
|
|
|
if (!use_attribute) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2021-09-16 20:49:10 -05:00
|
|
|
|
2023-08-24 13:36:25 +02:00
|
|
|
if (use_attribute->type == IDP_INT) {
|
|
|
|
|
IDP_Int(use_attribute) = !IDP_Int(use_attribute);
|
|
|
|
|
}
|
|
|
|
|
else if (use_attribute->type == IDP_BOOLEAN) {
|
|
|
|
|
IDP_Bool(use_attribute) = !IDP_Bool(use_attribute);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2021-09-16 20:49:10 -05:00
|
|
|
|
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_geometry_nodes_input_attribute_toggle(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Input Attribute Toggle";
|
|
|
|
|
ot->description =
|
|
|
|
|
"Switch between an attribute and a single value to define the data for every element";
|
|
|
|
|
ot->idname = "OBJECT_OT_geometry_nodes_input_attribute_toggle";
|
|
|
|
|
|
|
|
|
|
ot->exec = geometry_nodes_input_attribute_toggle_exec;
|
2022-08-28 20:08:18 +10:00
|
|
|
ot->poll = ED_operator_object_active;
|
2021-09-16 20:49:10 -05:00
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
|
2023-08-24 13:36:25 +02:00
|
|
|
RNA_def_string(ot->srna, "input_name", nullptr, 0, "Input Name", "");
|
2022-04-18 12:40:23 -05:00
|
|
|
RNA_def_string(ot->srna, "modifier_name", nullptr, MAX_NAME, "Modifier Name", "");
|
2021-09-16 20:49:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
2022-04-18 10:15:30 -05:00
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Copy and Assign Geometry Node Group operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int geometry_node_tree_copy_assign_exec(bContext *C, wmOperator * /*op*/)
|
2022-04-18 10:15:30 -05:00
|
|
|
{
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2022-04-18 10:15:30 -05:00
|
|
|
ModifierData *md = BKE_object_active_modifier(ob);
|
2022-08-28 20:08:18 +10:00
|
|
|
if (!(md && md->type == eModifierType_Nodes)) {
|
2022-04-18 10:15:30 -05:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NodesModifierData *nmd = (NodesModifierData *)md;
|
|
|
|
|
bNodeTree *tree = nmd->node_group;
|
2022-04-18 12:40:23 -05:00
|
|
|
if (tree == nullptr) {
|
2022-04-18 10:15:30 -05:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bNodeTree *new_tree = (bNodeTree *)BKE_id_copy_ex(
|
2022-04-18 12:40:23 -05:00
|
|
|
bmain, &tree->id, nullptr, LIB_ID_COPY_ACTIONS | LIB_ID_COPY_DEFAULT);
|
2022-04-18 10:15:30 -05:00
|
|
|
|
2023-09-08 10:03:08 -04:00
|
|
|
nmd->flag &= ~NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR;
|
|
|
|
|
|
2022-04-18 12:40:23 -05:00
|
|
|
if (new_tree == nullptr) {
|
2022-04-18 10:15:30 -05:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nmd->node_group = new_tree;
|
|
|
|
|
id_us_min(&tree->id);
|
|
|
|
|
|
2022-09-13 16:40:57 +02:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
2022-05-23 11:29:55 +02:00
|
|
|
DEG_relations_tag_update(bmain);
|
2022-04-18 10:15:30 -05:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Copy Geometry Node Group";
|
|
|
|
|
ot->description = "Copy the active geometry node group and assign it to the active modifier";
|
|
|
|
|
ot->idname = "OBJECT_OT_geometry_node_tree_copy_assign";
|
|
|
|
|
|
|
|
|
|
ot->exec = geometry_node_tree_copy_assign_exec;
|
2022-08-28 20:08:18 +10:00
|
|
|
ot->poll = ED_operator_object_active;
|
2022-04-18 10:15:30 -05:00
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
2024-02-08 15:35:20 +01:00
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Dash Modifier
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static bool dash_modifier_segment_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
return edit_modifier_poll_generic(C, &RNA_GreasePencilDashModifierData, 0, false, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int dash_modifier_segment_add_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2024-02-08 15:35:20 +01:00
|
|
|
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(
|
|
|
|
|
edit_modifier_property_get(op, ob, eModifierType_GreasePencilDash));
|
|
|
|
|
|
|
|
|
|
if (dmd == nullptr) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GreasePencilDashModifierSegment *new_segments = static_cast<GreasePencilDashModifierSegment *>(
|
|
|
|
|
MEM_malloc_arrayN(dmd->segments_num + 1, sizeof(GreasePencilDashModifierSegment), __func__));
|
|
|
|
|
|
2024-02-12 16:44:28 +01:00
|
|
|
const int new_active_index = std::clamp(dmd->segment_active_index + 1, 0, dmd->segments_num);
|
2024-02-08 15:35:20 +01:00
|
|
|
if (dmd->segments_num != 0) {
|
|
|
|
|
/* Copy the segments before the new segment. */
|
|
|
|
|
memcpy(new_segments,
|
|
|
|
|
dmd->segments_array,
|
|
|
|
|
sizeof(GreasePencilDashModifierSegment) * new_active_index);
|
|
|
|
|
/* Copy the segments after the new segment. */
|
|
|
|
|
memcpy(new_segments + new_active_index + 1,
|
|
|
|
|
dmd->segments_array + new_active_index,
|
|
|
|
|
sizeof(GreasePencilDashModifierSegment) * (dmd->segments_num - new_active_index));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create the new segment. */
|
|
|
|
|
GreasePencilDashModifierSegment *ds = &new_segments[new_active_index];
|
|
|
|
|
memcpy(ds,
|
|
|
|
|
DNA_struct_default_get(GreasePencilDashModifierSegment),
|
|
|
|
|
sizeof(GreasePencilDashModifierSegment));
|
|
|
|
|
BLI_uniquename_cb(
|
2024-03-28 01:30:38 +01:00
|
|
|
[&](const StringRef name) {
|
2024-02-08 15:35:20 +01:00
|
|
|
for (const GreasePencilDashModifierSegment &ds : dmd->segments()) {
|
|
|
|
|
if (STREQ(ds.name, name.data())) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
'.',
|
|
|
|
|
ds->name);
|
|
|
|
|
|
|
|
|
|
MEM_SAFE_FREE(dmd->segments_array);
|
|
|
|
|
dmd->segments_array = new_segments;
|
|
|
|
|
dmd->segments_num++;
|
2024-03-11 12:30:38 +01:00
|
|
|
dmd->segment_active_index = new_active_index;
|
2024-02-08 15:35:20 +01:00
|
|
|
|
2024-02-19 15:54:08 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
|
2024-02-08 15:35:20 +01:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int dash_modifier_segment_add_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
|
|
|
|
{
|
|
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
|
|
|
|
return dash_modifier_segment_add_exec(C, op);
|
|
|
|
|
}
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_grease_pencil_dash_modifier_segment_add(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Add Segment";
|
|
|
|
|
ot->description = "Add a segment to the dash modifier";
|
|
|
|
|
ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_add";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
2024-03-28 01:30:38 +01:00
|
|
|
ot->poll = dash_modifier_segment_poll;
|
|
|
|
|
ot->invoke = dash_modifier_segment_add_invoke;
|
|
|
|
|
ot->exec = dash_modifier_segment_add_exec;
|
2024-02-08 15:35:20 +01:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dash_modifier_segment_free(GreasePencilDashModifierSegment * /*ds*/) {}
|
|
|
|
|
|
|
|
|
|
static int dash_modifier_segment_remove_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2024-02-08 15:35:20 +01:00
|
|
|
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(
|
|
|
|
|
edit_modifier_property_get(op, ob, eModifierType_GreasePencilDash));
|
|
|
|
|
|
|
|
|
|
if (dmd == nullptr) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!dmd->segments().index_range().contains(dmd->segment_active_index)) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
dna::array::remove_index(&dmd->segments_array,
|
|
|
|
|
&dmd->segments_num,
|
|
|
|
|
&dmd->segment_active_index,
|
|
|
|
|
dmd->segment_active_index,
|
|
|
|
|
dash_modifier_segment_free);
|
2024-02-08 15:35:20 +01:00
|
|
|
|
2024-02-19 15:54:08 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
|
2024-02-08 15:35:20 +01:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int dash_modifier_segment_remove_invoke(bContext *C,
|
|
|
|
|
wmOperator *op,
|
|
|
|
|
const wmEvent * /*event*/)
|
|
|
|
|
{
|
|
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
|
|
|
|
return dash_modifier_segment_remove_exec(C, op);
|
|
|
|
|
}
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Remove Dash Segment";
|
|
|
|
|
ot->description = "Remove the active segment from the dash modifier";
|
|
|
|
|
ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_remove";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
2024-03-28 01:30:38 +01:00
|
|
|
ot->poll = dash_modifier_segment_poll;
|
|
|
|
|
ot->invoke = dash_modifier_segment_remove_invoke;
|
|
|
|
|
ot->exec = dash_modifier_segment_remove_exec;
|
2024-02-08 15:35:20 +01:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
|
|
|
|
|
RNA_def_int(
|
|
|
|
|
ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of the segment to remove", 0, INT_MAX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum class DashSegmentMoveDirection {
|
|
|
|
|
Up = -1,
|
|
|
|
|
Down = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int dash_modifier_segment_move_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2024-02-08 15:35:20 +01:00
|
|
|
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(
|
|
|
|
|
edit_modifier_property_get(op, ob, eModifierType_GreasePencilDash));
|
|
|
|
|
|
|
|
|
|
if (dmd == nullptr) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dmd->segments_num < 2) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const DashSegmentMoveDirection direction = DashSegmentMoveDirection(
|
|
|
|
|
RNA_enum_get(op->ptr, "type"));
|
|
|
|
|
switch (direction) {
|
|
|
|
|
case DashSegmentMoveDirection::Up:
|
|
|
|
|
if (dmd->segment_active_index == 0) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::swap(dmd->segments_array[dmd->segment_active_index],
|
|
|
|
|
dmd->segments_array[dmd->segment_active_index - 1]);
|
|
|
|
|
|
|
|
|
|
dmd->segment_active_index--;
|
|
|
|
|
break;
|
|
|
|
|
case DashSegmentMoveDirection::Down:
|
|
|
|
|
if (dmd->segment_active_index == dmd->segments_num - 1) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::swap(dmd->segments_array[dmd->segment_active_index],
|
|
|
|
|
dmd->segments_array[dmd->segment_active_index + 1]);
|
|
|
|
|
|
|
|
|
|
dmd->segment_active_index++;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 15:54:08 +01:00
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
|
2024-02-08 15:35:20 +01:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int dash_modifier_segment_move_invoke(bContext *C,
|
|
|
|
|
wmOperator *op,
|
|
|
|
|
const wmEvent * /*event*/)
|
|
|
|
|
{
|
|
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
|
|
|
|
return dash_modifier_segment_move_exec(C, op);
|
|
|
|
|
}
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_grease_pencil_dash_modifier_segment_move(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
static const EnumPropertyItem segment_move[] = {
|
|
|
|
|
{int(DashSegmentMoveDirection::Up), "UP", 0, "Up", ""},
|
|
|
|
|
{int(DashSegmentMoveDirection::Down), "DOWN", 0, "Down", ""},
|
|
|
|
|
{0, nullptr, 0, nullptr, nullptr},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Move Dash Segment";
|
|
|
|
|
ot->description = "Move the active dash segment up or down";
|
|
|
|
|
ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_move";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
2024-03-28 01:30:38 +01:00
|
|
|
ot->poll = dash_modifier_segment_poll;
|
|
|
|
|
ot->invoke = dash_modifier_segment_move_invoke;
|
|
|
|
|
ot->exec = dash_modifier_segment_move_exec;
|
2024-02-08 15:35:20 +01:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
|
|
|
|
|
ot->prop = RNA_def_enum(ot->srna, "type", segment_move, 0, "Type", "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
2024-02-27 14:17:22 +01:00
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
/** \name Time Modifier
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static bool time_modifier_segment_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
return edit_modifier_poll_generic(C, &RNA_GreasePencilTimeModifier, 0, false, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int time_modifier_segment_add_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2024-02-27 14:17:22 +01:00
|
|
|
auto *tmd = reinterpret_cast<GreasePencilTimeModifierData *>(
|
|
|
|
|
edit_modifier_property_get(op, ob, eModifierType_GreasePencilTime));
|
|
|
|
|
|
|
|
|
|
if (tmd == nullptr) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GreasePencilTimeModifierSegment *new_segments = static_cast<GreasePencilTimeModifierSegment *>(
|
|
|
|
|
MEM_malloc_arrayN(tmd->segments_num + 1, sizeof(GreasePencilTimeModifierSegment), __func__));
|
|
|
|
|
|
|
|
|
|
const int new_active_index = std::clamp(tmd->segment_active_index + 1, 0, tmd->segments_num);
|
|
|
|
|
if (tmd->segments_num != 0) {
|
|
|
|
|
/* Copy the segments before the new segment. */
|
|
|
|
|
memcpy(new_segments,
|
|
|
|
|
tmd->segments_array,
|
|
|
|
|
sizeof(GreasePencilTimeModifierSegment) * new_active_index);
|
|
|
|
|
/* Copy the segments after the new segment. */
|
|
|
|
|
memcpy(new_segments + new_active_index + 1,
|
|
|
|
|
tmd->segments_array + new_active_index,
|
|
|
|
|
sizeof(GreasePencilTimeModifierSegment) * (tmd->segments_num - new_active_index));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create the new segment. */
|
|
|
|
|
GreasePencilTimeModifierSegment *segment = &new_segments[new_active_index];
|
|
|
|
|
memcpy(segment,
|
|
|
|
|
DNA_struct_default_get(GreasePencilTimeModifierSegment),
|
|
|
|
|
sizeof(GreasePencilTimeModifierSegment));
|
|
|
|
|
BLI_uniquename_cb(
|
2024-03-28 01:30:38 +01:00
|
|
|
[&](const StringRef name) {
|
2024-02-27 14:17:22 +01:00
|
|
|
for (const GreasePencilTimeModifierSegment &segment : tmd->segments()) {
|
|
|
|
|
if (STREQ(segment.name, name.data())) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
'.',
|
|
|
|
|
segment->name);
|
|
|
|
|
|
|
|
|
|
MEM_SAFE_FREE(tmd->segments_array);
|
|
|
|
|
tmd->segments_array = new_segments;
|
|
|
|
|
tmd->segments_num++;
|
|
|
|
|
tmd->segment_active_index++;
|
|
|
|
|
|
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int time_modifier_segment_add_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
|
|
|
|
{
|
|
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
|
|
|
|
return time_modifier_segment_add_exec(C, op);
|
|
|
|
|
}
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_grease_pencil_time_modifier_segment_add(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Add Segment";
|
|
|
|
|
ot->description = "Add a segment to the time modifier";
|
|
|
|
|
ot->idname = "OBJECT_OT_grease_pencil_time_modifier_segment_add";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
2024-03-28 01:30:38 +01:00
|
|
|
ot->poll = time_modifier_segment_poll;
|
|
|
|
|
ot->invoke = time_modifier_segment_add_invoke;
|
|
|
|
|
ot->exec = time_modifier_segment_add_exec;
|
2024-02-27 14:17:22 +01:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void time_modifier_segment_free(GreasePencilTimeModifierSegment * /*ds*/) {}
|
|
|
|
|
|
|
|
|
|
static int time_modifier_segment_remove_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2024-02-27 14:17:22 +01:00
|
|
|
auto *tmd = reinterpret_cast<GreasePencilTimeModifierData *>(
|
|
|
|
|
edit_modifier_property_get(op, ob, eModifierType_GreasePencilTime));
|
|
|
|
|
|
|
|
|
|
if (tmd == nullptr) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!tmd->segments().index_range().contains(tmd->segment_active_index)) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 01:30:38 +01:00
|
|
|
dna::array::remove_index(&tmd->segments_array,
|
|
|
|
|
&tmd->segments_num,
|
|
|
|
|
&tmd->segment_active_index,
|
|
|
|
|
tmd->segment_active_index,
|
|
|
|
|
time_modifier_segment_free);
|
2024-02-27 14:17:22 +01:00
|
|
|
|
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int time_modifier_segment_remove_invoke(bContext *C,
|
|
|
|
|
wmOperator *op,
|
|
|
|
|
const wmEvent * /*event*/)
|
|
|
|
|
{
|
|
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
|
|
|
|
return time_modifier_segment_remove_exec(C, op);
|
|
|
|
|
}
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_grease_pencil_time_modifier_segment_remove(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Remove Segment";
|
|
|
|
|
ot->description = "Remove the active segment from the time modifier";
|
|
|
|
|
ot->idname = "OBJECT_OT_grease_pencil_time_modifier_segment_remove";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
2024-03-28 01:30:38 +01:00
|
|
|
ot->poll = time_modifier_segment_poll;
|
|
|
|
|
ot->invoke = time_modifier_segment_remove_invoke;
|
|
|
|
|
ot->exec = time_modifier_segment_remove_exec;
|
2024-02-27 14:17:22 +01:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
|
|
|
|
|
RNA_def_int(
|
|
|
|
|
ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of the segment to remove", 0, INT_MAX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum class TimeSegmentMoveDirection {
|
|
|
|
|
Up = -1,
|
|
|
|
|
Down = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int time_modifier_segment_move_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2024-03-28 01:30:38 +01:00
|
|
|
Object *ob = context_active_object(C);
|
2024-02-27 14:17:22 +01:00
|
|
|
auto *tmd = reinterpret_cast<GreasePencilTimeModifierData *>(
|
|
|
|
|
edit_modifier_property_get(op, ob, eModifierType_GreasePencilTime));
|
|
|
|
|
|
|
|
|
|
if (tmd == nullptr) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tmd->segments_num < 2) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const TimeSegmentMoveDirection direction = TimeSegmentMoveDirection(
|
|
|
|
|
RNA_enum_get(op->ptr, "type"));
|
|
|
|
|
switch (direction) {
|
|
|
|
|
case TimeSegmentMoveDirection::Up:
|
|
|
|
|
if (tmd->segment_active_index == 0) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::swap(tmd->segments_array[tmd->segment_active_index],
|
|
|
|
|
tmd->segments_array[tmd->segment_active_index - 1]);
|
|
|
|
|
|
|
|
|
|
tmd->segment_active_index--;
|
|
|
|
|
break;
|
|
|
|
|
case TimeSegmentMoveDirection::Down:
|
|
|
|
|
if (tmd->segment_active_index == tmd->segments_num - 1) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::swap(tmd->segments_array[tmd->segment_active_index],
|
|
|
|
|
tmd->segments_array[tmd->segment_active_index + 1]);
|
|
|
|
|
|
|
|
|
|
tmd->segment_active_index++;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int time_modifier_segment_move_invoke(bContext *C,
|
|
|
|
|
wmOperator *op,
|
|
|
|
|
const wmEvent * /*event*/)
|
|
|
|
|
{
|
|
|
|
|
if (edit_modifier_invoke_properties(C, op)) {
|
|
|
|
|
return time_modifier_segment_move_exec(C, op);
|
|
|
|
|
}
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBJECT_OT_grease_pencil_time_modifier_segment_move(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
static const EnumPropertyItem segment_move[] = {
|
|
|
|
|
{int(TimeSegmentMoveDirection::Up), "UP", 0, "Up", ""},
|
|
|
|
|
{int(TimeSegmentMoveDirection::Down), "DOWN", 0, "Down", ""},
|
|
|
|
|
{0, nullptr, 0, nullptr, nullptr},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Move Segment";
|
|
|
|
|
ot->description = "Move the active time segment up or down";
|
|
|
|
|
ot->idname = "OBJECT_OT_grease_pencil_time_modifier_segment_move";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
2024-03-28 01:30:38 +01:00
|
|
|
ot->poll = time_modifier_segment_poll;
|
|
|
|
|
ot->invoke = time_modifier_segment_move_invoke;
|
|
|
|
|
ot->exec = time_modifier_segment_move_exec;
|
2024-02-27 14:17:22 +01:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
edit_modifier_properties(ot);
|
|
|
|
|
|
|
|
|
|
ot->prop = RNA_def_enum(ot->srna, "type", segment_move, 0, "Type", "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
2024-03-28 01:30:38 +01:00
|
|
|
|
|
|
|
|
} // namespace blender::ed::object
|